From 8dee6d3c3c23d07639b70ed665b413b2a9ab9895 Mon Sep 17 00:00:00 2001 From: James Frowen Date: Sat, 30 May 2026 21:48:17 +0100 Subject: [PATCH 01/41] refactor: support properties in Weaver and delete PropertySiteProcessor --- .../NetworkBehaviour/FoundNetworkBehaviour.cs | 4 +- .../Processors/NetworkBehaviourProcessor.cs | 4 +- .../Processors/PropertySiteProcessor.cs | 160 ------------- .../Weaver/Processors/SyncVarProcessor.cs | 217 +++++++++++------- .../Processors/SyncVars/FoundSyncVar.cs | 30 +-- .../Processors/SyncVars/HookMethodFinder.cs | 24 +- Assets/Mirage/Weaver/Weaver.cs | 9 +- 7 files changed, 157 insertions(+), 291 deletions(-) delete mode 100644 Assets/Mirage/Weaver/Processors/PropertySiteProcessor.cs diff --git a/Assets/Mirage/Weaver/Processors/NetworkBehaviour/FoundNetworkBehaviour.cs b/Assets/Mirage/Weaver/Processors/NetworkBehaviour/FoundNetworkBehaviour.cs index ad418139419..a48669d091e 100644 --- a/Assets/Mirage/Weaver/Processors/NetworkBehaviour/FoundNetworkBehaviour.cs +++ b/Assets/Mirage/Weaver/Processors/NetworkBehaviour/FoundNetworkBehaviour.cs @@ -20,10 +20,10 @@ public FoundNetworkBehaviour(ModuleDefinition module, TypeDefinition td) public List SyncVars { get; private set; } = new List(); - public FoundSyncVar AddSyncVar(FieldDefinition fd) + public FoundSyncVar AddSyncVar(PropertyDefinition pd, FieldDefinition fd) { var dirtyIndex = syncVarCounter.GetInBase() + SyncVars.Count; - var syncVar = new FoundSyncVar(Module, this, fd, dirtyIndex); + var syncVar = new FoundSyncVar(Module, this, pd, fd, dirtyIndex); SyncVars.Add(syncVar); return syncVar; } diff --git a/Assets/Mirage/Weaver/Processors/NetworkBehaviourProcessor.cs b/Assets/Mirage/Weaver/Processors/NetworkBehaviourProcessor.cs index a02dbe4f1de..78538bce415 100644 --- a/Assets/Mirage/Weaver/Processors/NetworkBehaviourProcessor.cs +++ b/Assets/Mirage/Weaver/Processors/NetworkBehaviourProcessor.cs @@ -32,14 +32,14 @@ internal class NetworkBehaviourProcessor private readonly SyncObjectProcessor syncObjectProcessor; private readonly ConstFieldTracker rpcCounter; - public NetworkBehaviourProcessor(TypeDefinition td, Readers readers, Writers writers, PropertySiteProcessor propertySiteProcessor, IWeaverLogger logger) + public NetworkBehaviourProcessor(TypeDefinition td, Readers readers, Writers writers, IWeaverLogger logger) { Weaver.DebugLog(td, "NetworkBehaviourProcessor"); netBehaviourSubclass = td; this.logger = logger; serverRpcProcessor = new ServerRpcProcessor(netBehaviourSubclass.Module, readers, writers, logger); clientRpcProcessor = new ClientRpcProcessor(netBehaviourSubclass.Module, readers, writers, logger); - syncVarProcessor = new SyncVarProcessor(netBehaviourSubclass.Module, readers, writers, propertySiteProcessor); + syncVarProcessor = new SyncVarProcessor(netBehaviourSubclass.Module, readers, writers); syncObjectProcessor = new SyncObjectProcessor(readers, writers, logger); // no max for rpcs, index is sent as var int, so more rpc just means bigger header size (still smaller than 4 byte hash) diff --git a/Assets/Mirage/Weaver/Processors/PropertySiteProcessor.cs b/Assets/Mirage/Weaver/Processors/PropertySiteProcessor.cs deleted file mode 100644 index 4bdab382323..00000000000 --- a/Assets/Mirage/Weaver/Processors/PropertySiteProcessor.cs +++ /dev/null @@ -1,160 +0,0 @@ -using System.Collections.Generic; -using Mirage.CodeGen; -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace Mirage.Weaver -{ - /// - /// Replaces SyncVar fields with their property getter/setting - /// - public class PropertySiteProcessor - { - // setter functions that replace [SyncVar] member variable references. dict - public Dictionary Setters = new Dictionary(new FieldReferenceComparator()); - // getter functions that replace [SyncVar] member variable references. dict - public Dictionary Getters = new Dictionary(new FieldReferenceComparator()); - - public void Process(ModuleDefinition moduleDef) - { - // replace all field access with property access for syncvars - CodePass.ForEachInstruction(moduleDef, WeavedMethods, ProcessInstruction); - } - - private static bool WeavedMethods(MethodDefinition md) - { - if (md.Name == ".cctor") - return false; - if (md.Name == NetworkBehaviourProcessor.ProcessedFunctionName) - return false; - - // dont use network get/set inside unity consturctors - // this is because they will try to set dirtyBit and throw unity errors - if (md.DeclaringType.IsDerivedFrom() && md.IsConstructor) - return false; - - // note: Constructor for non-unity types should be safe to use, for example get/set a sync var on a NB from without a struct - - return true; - } - - private Instruction ProcessInstruction(MethodDefinition md, Instruction instr, SequencePoint sequencePoint) - { - if (instr.OpCode == OpCodes.Stfld && instr.Operand is FieldReference opFieldst) - { - FieldReference resolved = opFieldst.Resolve(); - if (resolved == null) - { - resolved = opFieldst.DeclaringType.Resolve().GetField(opFieldst.Name); - } - - // this instruction sets the value of a field. cache the field reference. - ProcessInstructionSetterField(instr, resolved); - } - - if (instr.OpCode == OpCodes.Ldfld && instr.Operand is FieldReference opFieldld) - { - FieldReference resolved = opFieldld.Resolve(); - if (resolved == null) - { - resolved = opFieldld.DeclaringType.Resolve().GetField(opFieldld.Name); - } - - // this instruction gets the value of a field. cache the field reference. - ProcessInstructionGetterField(instr, resolved); - } - - if (instr.OpCode == OpCodes.Ldflda && instr.Operand is FieldReference opFieldlda) - { - FieldReference resolved = opFieldlda.Resolve(); - if (resolved == null) - { - resolved = opFieldlda.DeclaringType.Resolve().GetField(opFieldlda.Name); - } - - // loading a field by reference, watch out for initobj instruction - // see https://github.com/vis2k/Mirror/issues/696 - return ProcessInstructionLoadAddress(md, instr, resolved); - } - - return instr; - } - - // replaces syncvar write access with the NetworkXYZ.get property calls - private void ProcessInstructionSetterField(Instruction i, FieldReference opField) - { - // does it set a field that we replaced? - if (Setters.TryGetValue(opField, out var replacement)) - { - if (opField.DeclaringType.IsGenericInstance || opField.DeclaringType.HasGenericParameters) // We're calling to a generic class - { - var newField = i.Operand as FieldReference; - var genericType = (GenericInstanceType)newField.DeclaringType; - i.OpCode = OpCodes.Callvirt; - i.Operand = replacement.MakeHostInstanceGeneric(genericType); - } - else - { - //replace with property - i.OpCode = OpCodes.Call; - i.Operand = replacement; - } - } - } - - // replaces syncvar read access with the NetworkXYZ.get property calls - private void ProcessInstructionGetterField(Instruction i, FieldReference opField) - { - // does it set a field that we replaced? - if (Getters.TryGetValue(opField, out var replacement)) - { - if (opField.DeclaringType.IsGenericInstance || opField.DeclaringType.HasGenericParameters) // We're calling to a generic class - { - var newField = i.Operand as FieldReference; - var genericType = (GenericInstanceType)newField.DeclaringType; - i.OpCode = OpCodes.Callvirt; - i.Operand = replacement.MakeHostInstanceGeneric(genericType); - } - else - { - //replace with property - i.OpCode = OpCodes.Call; - i.Operand = replacement; - } - } - } - - private Instruction ProcessInstructionLoadAddress(MethodDefinition md, Instruction instr, FieldReference opField) - { - // does it set a field that we replaced? - if (Setters.TryGetValue(opField, out var replacement)) - { - // we have a replacement for this property - // is the next instruction a initobj? - var nextInstr = instr.Next; - - if (nextInstr.OpCode == OpCodes.Initobj) - { - // we need to replace this code with: - // var tmp = new MyStruct(); - // this.set_Networkxxxx(tmp); - var worker = md.Body.GetILProcessor(); - var tmpVariable = md.AddLocal(opField.FieldType); - - worker.InsertBefore(instr, worker.Create(OpCodes.Ldloca, tmpVariable)); - worker.InsertBefore(instr, worker.Create(OpCodes.Initobj, opField.FieldType)); - worker.InsertBefore(instr, worker.Create(OpCodes.Ldloc, tmpVariable)); - var newInstr = worker.Create(OpCodes.Call, replacement); - worker.InsertBefore(instr, newInstr); - - worker.Remove(instr); - worker.Remove(nextInstr); - - return newInstr; - } - } - - return instr; - } - } -} diff --git a/Assets/Mirage/Weaver/Processors/SyncVarProcessor.cs b/Assets/Mirage/Weaver/Processors/SyncVarProcessor.cs index 8c0d61f462b..44eda8f7d10 100644 --- a/Assets/Mirage/Weaver/Processors/SyncVarProcessor.cs +++ b/Assets/Mirage/Weaver/Processors/SyncVarProcessor.cs @@ -19,16 +19,14 @@ public class SyncVarProcessor private readonly ModuleDefinition module; private readonly Readers readers; private readonly Writers writers; - private readonly PropertySiteProcessor propertySiteProcessor; private FoundNetworkBehaviour behaviour; - public SyncVarProcessor(ModuleDefinition module, Readers readers, Writers writers, PropertySiteProcessor propertySiteProcessor) + public SyncVarProcessor(ModuleDefinition module, Readers readers, Writers writers) { this.module = module; this.readers = readers; this.writers = writers; - this.propertySiteProcessor = propertySiteProcessor; } public void ProcessSyncVars(TypeDefinition td, IWeaverLogger logger) @@ -38,23 +36,25 @@ public void ProcessSyncVars(TypeDefinition td, IWeaverLogger logger) // start assigning syncvars at the place the base class stopped, if any // find syncvars - // use ToArray to create copy, ProcessSyncVar might add new fields - foreach (var fd in td.Fields.ToArray()) + // use ToArray to create copy + foreach (var pd in td.Properties.ToArray()) { - // try/catch for each field, and log once - // we dont want to spam multiple logs for a single field + // try/catch for each property, and log once + // we dont want to spam multiple logs for a single property try { - if (IsValidSyncVar(fd)) + if (IsValidSyncVar(pd, out var fd)) { - var syncVar = behaviour.AddSyncVar(fd); + InjectAndCopyAttributes(pd, fd); + + var syncVar = behaviour.AddSyncVar(pd, fd); ProcessSyncVar(syncVar); syncVar.HasProcessed = true; } } catch (ValueSerializerException e) { - logger.Error(e.Message, fd); + logger.Error(e.Message, pd); } catch (SyncVarException e) { @@ -62,8 +62,8 @@ public void ProcessSyncVars(TypeDefinition td, IWeaverLogger logger) } catch (SerializeFunctionException e) { - // use field as member referecne - logger.Error(e.Message, fd); + // use property as member reference + logger.Error(e.Message, pd); } } @@ -73,30 +73,113 @@ public void ProcessSyncVars(TypeDefinition td, IWeaverLogger logger) GenerateDeserialization(); } - private bool IsValidSyncVar(FieldDefinition field) + private bool IsValidSyncVar(PropertyDefinition property, out FieldDefinition backingField) { - if (!field.HasCustomAttribute()) - { + backingField = null; + if (!property.HasCustomAttribute()) + return false; + + if (property.GetMethod == null || property.SetMethod == null) + throw new SyncVarException($"{property.Name} must have both get and set accessors", property); + + if (property.GetMethod.IsStatic || property.SetMethod.IsStatic) + throw new SyncVarException($"{property.Name} cannot be static", property); + + var backingFieldName = $"<{property.Name}>k__BackingField"; + backingField = property.DeclaringType.Fields.FirstOrDefault(f => f.Name == backingFieldName); + if (backingField == null) + throw new SyncVarException($"{property.Name} is not an auto-property (no backing field found)", property); + + if (property.PropertyType.IsArray) + throw new SyncVarException($"{property.Name} has invalid type. Use SyncLists instead of arrays", property); + + if (SyncObjectProcessor.ImplementsSyncObject(property.PropertyType)) + throw new SyncVarException($"{property.Name} has [SyncVar] attribute. ISyncObject should not be marked with SyncVar", property); + + if (!IsSimpleGetter(property, backingField)) + throw new SyncVarException($"{property.Name} does not have a simple auto-property getter", property); + + if (!IsSimpleSetter(property, backingField)) + throw new SyncVarException($"{property.Name} does not have a simple auto-property setter", property); + + return true; + } + + private bool IsSimpleGetter(PropertyDefinition property, FieldDefinition backingField) + { + if (property.GetMethod?.Body == null) return false; + + var ldfldCount = 0; + foreach (var inst in property.GetMethod.Body.Instructions) + { + if (inst.OpCode == OpCodes.Ldfld) + { + if ((inst.Operand as FieldReference)?.Resolve() != backingField) + return false; + ldfldCount++; + } + else if (inst.OpCode == OpCodes.Stfld || inst.OpCode == OpCodes.Ldsfld || inst.OpCode == OpCodes.Stsfld) + return false; } - if ((field.Attributes & FieldAttributes.Static) != 0) + return ldfldCount == 1; + } + + private bool IsSimpleSetter(PropertyDefinition property, FieldDefinition backingField) + { + if (property.SetMethod?.Body == null) + return false; + + var stfldCount = 0; + foreach (var inst in property.SetMethod.Body.Instructions) { - throw new SyncVarException($"{field.Name} cannot be static", field); + if (inst.OpCode == OpCodes.Stfld) + { + if ((inst.Operand as FieldReference)?.Resolve() != backingField) + return false; + stfldCount++; + } + else if (inst.OpCode == OpCodes.Ldfld || inst.OpCode == OpCodes.Ldsfld || inst.OpCode == OpCodes.Stsfld) + return false; } - if (field.FieldType.IsArray) + return stfldCount == 1; + } + + private void InjectAndCopyAttributes(PropertyDefinition property, FieldDefinition backingField) + { + if (!backingField.CustomAttributes.Any(a => a.AttributeType.FullName == typeof(UnityEngine.SerializeField).FullName)) { - // todo should arrays really be blocked? - throw new SyncVarException($"{field.Name} has invalid type. Use SyncLists instead of arrays", field); + var serializeFieldConstructor = typeof(UnityEngine.SerializeField).GetConstructor(Type.EmptyTypes); + var serializeFieldAttribute = new CustomAttribute(module.ImportReference(serializeFieldConstructor)); + backingField.CustomAttributes.Add(serializeFieldAttribute); } - if (SyncObjectProcessor.ImplementsSyncObject(field.FieldType)) + foreach (var attr in property.CustomAttributes) { - throw new SyncVarException($"{field.Name} has [SyncVar] attribute. ISyncObject should not be marked with SyncVar", field); + var attrType = attr.AttributeType; + if (attrType.Is() || attrType.Namespace == "UnityEngine") + { + if (!backingField.CustomAttributes.Any(a => a.AttributeType.FullName == attrType.FullName)) + CopyAttribute(attr, backingField); + } } + } - return true; + private void CopyAttribute(CustomAttribute source, FieldDefinition targetField) + { + var newAttr = new CustomAttribute(module.ImportReference(source.Constructor)); + foreach (var arg in source.ConstructorArguments) + newAttr.ConstructorArguments.Add(new CustomAttributeArgument(module.ImportReference(arg.Type), arg.Value)); + + foreach (var field in source.Fields) + newAttr.Fields.Add(new CustomAttributeNamedArgument(field.Name, new CustomAttributeArgument(module.ImportReference(field.Argument.Type), field.Argument.Value))); + + foreach (var prop in source.Properties) + newAttr.Properties.Add(new CustomAttributeNamedArgument(prop.Name, new CustomAttributeArgument(module.ImportReference(prop.Argument.Type), prop.Argument.Value))); + + targetField.CustomAttributes.Add(newAttr); } private void ProcessSyncVar(FoundSyncVar syncVar) @@ -105,91 +188,49 @@ private void ProcessSyncVar(FoundSyncVar syncVar) syncVar.SetWrapType(); syncVar.ProcessAttributes(writers, readers); - var fd = syncVar.FieldDefinition; + var pd = syncVar.PropertyDefinition; + Weaver.DebugLog(pd.DeclaringType, $"Sync Var {pd.Name} {pd.PropertyType}"); - var originalName = fd.Name; - Weaver.DebugLog(fd.DeclaringType, $"Sync Var {fd.Name} {fd.FieldType}"); - - var get = GenerateSyncVarGetter(syncVar); - var set = syncVar.InitialOnly - ? GenerateSyncVarSetterInitialOnly(syncVar) - : GenerateSyncVarSetter(syncVar); - - //NOTE: is property even needed? Could just use a setter function? - //create the property - var propertyDefinition = new PropertyDefinition("Network" + originalName, PropertyAttributes.None, syncVar.OriginalType) - { - GetMethod = get, - SetMethod = set - }; - - propertyDefinition.DeclaringType = fd.DeclaringType; - //add the methods and property to the type. - fd.DeclaringType.Properties.Add(propertyDefinition); - propertySiteProcessor.Setters[fd] = set; - - if (syncVar.IsWrapped) - { - propertySiteProcessor.Getters[fd] = get; - } + GenerateSyncVarGetter(syncVar); + if (syncVar.InitialOnly) + GenerateSyncVarSetterInitialOnly(syncVar); + else + GenerateSyncVarSetter(syncVar); } - private MethodDefinition GenerateSyncVarGetter(FoundSyncVar syncVar) + private void GenerateSyncVarGetter(FoundSyncVar syncVar) { - var fd = syncVar.FieldDefinition; - var originalType = syncVar.OriginalType; - var originalName = syncVar.OriginalName; - - //Create the get method - var get = fd.DeclaringType.AddMethod( - "get_Network" + originalName, MethodAttributes.Public | - MethodAttributes.SpecialName | - MethodAttributes.HideBySig, - originalType); + var get = syncVar.PropertyDefinition.GetMethod; + get.Body.Instructions.Clear(); + get.Body.Variables.Clear(); var worker = get.Body.GetILProcessor(); WriteLoadField(worker, syncVar); worker.Append(worker.Create(OpCodes.Ret)); - - get.SemanticsAttributes = MethodSemanticsAttributes.Getter; - - return get; } - private MethodDefinition GenerateSyncVarSetterInitialOnly(FoundSyncVar syncVar) + private void GenerateSyncVarSetterInitialOnly(FoundSyncVar syncVar) { - // todo reduce duplicate code with this and GenerateSyncVarSetter - var fd = syncVar.FieldDefinition; - var originalType = syncVar.OriginalType; - var originalName = syncVar.OriginalName; + var set = syncVar.PropertyDefinition.SetMethod; + set.Body.Instructions.Clear(); + set.Body.Variables.Clear(); - //Create the set method - var set = fd.DeclaringType.AddMethod("set_Network" + originalName, MethodAttributes.Public | - MethodAttributes.SpecialName | - MethodAttributes.HideBySig); - var valueParam = set.AddParam(originalType, "value"); - set.SemanticsAttributes = MethodSemanticsAttributes.Setter; + var valueParam = set.Parameters[0]; var worker = set.Body.GetILProcessor(); WriteStoreField(worker, valueParam, syncVar); worker.Append(worker.Create(OpCodes.Ret)); - - return set; } - private MethodDefinition GenerateSyncVarSetter(FoundSyncVar syncVar) + private void GenerateSyncVarSetter(FoundSyncVar syncVar) { - var fd = syncVar.FieldDefinition; - var originalType = syncVar.OriginalType; - var originalName = syncVar.OriginalName; + var set = syncVar.PropertyDefinition.SetMethod; + set.Body.Instructions.Clear(); + set.Body.Variables.Clear(); - //Create the set method - var set = fd.DeclaringType.AddMethod("set_Network" + originalName, MethodAttributes.Public | - MethodAttributes.SpecialName | - MethodAttributes.HideBySig); - var valueParam = set.AddParam(originalType, "value"); - set.SemanticsAttributes = MethodSemanticsAttributes.Setter; + var originalType = syncVar.OriginalType; + var valueParam = set.Parameters[0]; var worker = set.Body.GetILProcessor(); @@ -278,8 +319,6 @@ private MethodDefinition GenerateSyncVarSetter(FoundSyncVar syncVar) worker.Append(endOfMethod); worker.Append(worker.Create(OpCodes.Ret)); - - return set; } /// diff --git a/Assets/Mirage/Weaver/Processors/SyncVars/FoundSyncVar.cs b/Assets/Mirage/Weaver/Processors/SyncVars/FoundSyncVar.cs index f0ce03c7e4a..ac00a11b49d 100644 --- a/Assets/Mirage/Weaver/Processors/SyncVars/FoundSyncVar.cs +++ b/Assets/Mirage/Weaver/Processors/SyncVars/FoundSyncVar.cs @@ -11,6 +11,7 @@ internal class FoundSyncVar public readonly ModuleDefinition Module; public readonly FoundNetworkBehaviour Behaviour; public readonly FieldDefinition FieldDefinition; + public readonly PropertyDefinition PropertyDefinition; public readonly int DirtyIndex; public long DirtyBit => 1L << DirtyIndex; @@ -20,10 +21,11 @@ internal class FoundSyncVar /// public bool HasProcessed { get; set; } = false; - public FoundSyncVar(ModuleDefinition module, FoundNetworkBehaviour behaviour, FieldDefinition fieldDefinition, int dirtyIndex) + public FoundSyncVar(ModuleDefinition module, FoundNetworkBehaviour behaviour, PropertyDefinition propertyDefinition, FieldDefinition fieldDefinition, int dirtyIndex) { Module = module; Behaviour = behaviour; + PropertyDefinition = propertyDefinition; FieldDefinition = fieldDefinition; DirtyIndex = dirtyIndex; } @@ -46,8 +48,8 @@ public FoundSyncVar(ModuleDefinition module, FoundNetworkBehaviour behaviour, Fi /// public void SetWrapType() { - OriginalName = FieldDefinition.Name; - OriginalType = FieldDefinition.FieldType; + OriginalName = PropertyDefinition.Name; + OriginalType = PropertyDefinition.PropertyType; if (CheckWrapType(OriginalType, out var wrapType)) { @@ -89,35 +91,35 @@ private bool CheckWrapType(TypeReference originalType, out TypeReference wrapTyp /// public void ProcessAttributes(Writers writers, Readers readers) { - var hook = HookMethodFinder.GetHookMethod(FieldDefinition, OriginalType); + var hook = HookMethodFinder.GetHookMethod(PropertyDefinition, OriginalType); Hook = hook; HasHook = hook != null; - InitialOnly = GetInitialOnly(FieldDefinition); - InvokeHookOnServer = GetFireOnServer(FieldDefinition); - InvokeHookOnOwner = GetFireOnOwner(FieldDefinition); + InitialOnly = GetInitialOnly(PropertyDefinition); + InvokeHookOnServer = GetFireOnServer(PropertyDefinition); + InvokeHookOnOwner = GetFireOnOwner(PropertyDefinition); ValueSerializer = ValueSerializerFinder.GetSerializer(this, writers, readers); if (!HasHook && (InvokeHookOnServer || InvokeHookOnOwner)) - throw new HookMethodException("'invokeHookOnServer' or 'InvokeHookOnOwner' is set to true but no hook was implemented. Please implement hook or set 'invokeHookOnServer' back to false or remove for default false.", FieldDefinition); + throw new HookMethodException("'invokeHookOnServer' or 'InvokeHookOnOwner' is set to true but no hook was implemented. Please implement hook or set 'invokeHookOnServer' back to false or remove for default false.", PropertyDefinition); } - private static bool GetInitialOnly(FieldDefinition fieldDefinition) + private static bool GetInitialOnly(PropertyDefinition propertyDefinition) { - var attr = fieldDefinition.GetCustomAttribute(); + var attr = propertyDefinition.GetCustomAttribute(); return attr.GetField(nameof(SyncVarAttribute.initialOnly), false); } - private static bool GetFireOnServer(FieldDefinition fieldDefinition) + private static bool GetFireOnServer(PropertyDefinition propertyDefinition) { - var attr = fieldDefinition.GetCustomAttribute(); + var attr = propertyDefinition.GetCustomAttribute(); return attr.GetField(nameof(SyncVarAttribute.invokeHookOnServer), false); } - private static bool GetFireOnOwner(FieldDefinition fieldDefinition) + private static bool GetFireOnOwner(PropertyDefinition propertyDefinition) { - var attr = fieldDefinition.GetCustomAttribute(); + var attr = propertyDefinition.GetCustomAttribute(); return attr.GetField(nameof(SyncVarAttribute.invokeHookOnOwner), false); } } diff --git a/Assets/Mirage/Weaver/Processors/SyncVars/HookMethodFinder.cs b/Assets/Mirage/Weaver/Processors/SyncVars/HookMethodFinder.cs index 738b5da5096..be770f458ed 100644 --- a/Assets/Mirage/Weaver/Processors/SyncVars/HookMethodFinder.cs +++ b/Assets/Mirage/Weaver/Processors/SyncVars/HookMethodFinder.cs @@ -28,7 +28,7 @@ internal static class HookMethodFinder { /// Found Hook method or null /// Throws if users sets hook in attribute but method could not be found - public static SyncVarHook GetHookMethod(FieldDefinition syncVar, TypeReference originalType) + public static SyncVarHook GetHookMethod(PropertyDefinition syncVar, TypeReference originalType) { var syncVarAttr = syncVar.GetCustomAttribute(); @@ -49,7 +49,7 @@ public static SyncVarHook GetHookMethod(FieldDefinition syncVar, TypeReference o throw new HookMethodException($"Could not find hook for '{syncVar.Name}', hook name '{hookFunctionName}', hook type {hookType}. See SyncHookType for valid signatures", syncVar); } - private static SyncVarHook FindHookMethod(FieldDefinition syncVar, string hookFunctionName, SyncHookType hookType, TypeReference originalType) + private static SyncVarHook FindHookMethod(PropertyDefinition syncVar, string hookFunctionName, SyncHookType hookType, TypeReference originalType) { switch (hookType) { @@ -67,7 +67,7 @@ private static SyncVarHook FindHookMethod(FieldDefinition syncVar, string hookFu } } - private static SyncVarHook FindAutomatic(FieldDefinition syncVar, string hookFunctionName, TypeReference originalType) + private static SyncVarHook FindAutomatic(PropertyDefinition syncVar, string hookFunctionName, TypeReference originalType) { SyncVarHook foundHook = null; @@ -81,30 +81,24 @@ private static SyncVarHook FindAutomatic(FieldDefinition syncVar, string hookFun return foundHook; } - private static void CheckHook(FieldDefinition syncVar, string hookFunctionName, ref SyncVarHook foundHook, SyncVarHook newfound) + private static void CheckHook(PropertyDefinition syncVar, string hookFunctionName, ref SyncVarHook foundHook, SyncVarHook newfound) { // dont need to check anything if new one is null (not found) if (newfound == null) return; if (foundHook == null) - { foundHook = newfound; - } else - { throw new HookMethodException($"Mutliple hooks found for '{syncVar.Name}', hook name '{hookFunctionName}'. Please set HookType or remove one of the overloads", syncVar); - } } - private static SyncVarHook FindMethod(FieldDefinition syncVar, TypeReference originalType, string hookFunctionName, int argCount) + private static SyncVarHook FindMethod(PropertyDefinition syncVar, TypeReference originalType, string hookFunctionName, int argCount) { var methods = syncVar.DeclaringType.GetMethods(hookFunctionName); var methodsWithParams = methods.Where(m => m.Parameters.Count == argCount).ToArray(); if (methodsWithParams.Length == 0) - { return null; - } // return method if matching args are found foreach (var method in methodsWithParams) @@ -119,7 +113,7 @@ private static SyncVarHook FindMethod(FieldDefinition syncVar, TypeReference ori throw new HookMethodException($"Wrong type for Parameter in hook for '{syncVar.Name}', hook name '{hookFunctionName}'.", syncVar, methods.First()); } - private static SyncVarHook FindEvent(FieldDefinition syncVar, TypeReference originalType, string hookFunctionName, int? argCount) + private static SyncVarHook FindEvent(PropertyDefinition syncVar, TypeReference originalType, string hookFunctionName, int? argCount) { // we can't have 2 events/fields with same name, so using `First` is ok here var @event = syncVar.DeclaringType.Events.FirstOrDefault(x => x.Name == hookFunctionName); @@ -128,9 +122,7 @@ private static SyncVarHook FindEvent(FieldDefinition syncVar, TypeReference orig var eventType = @event.EventType; if (!eventType.FullName.Contains("System.Action")) - { ThrowWrongHookType(syncVar, @event, eventType, "Not System.Action"); - } // if it is not generic, then it has no args if (!eventType.IsGenericInstance) @@ -161,14 +153,12 @@ private static SyncVarHook FindEvent(FieldDefinition syncVar, TypeReference orig // check param types if (!MatchesParameters(genericEvent, originalType, args.Count)) - { ThrowWrongHookType(syncVar, @event, eventType, "Param mismatch"); - } return new SyncVarHook(@event, args.Count); } - private static void ThrowWrongHookType(FieldDefinition syncVar, EventDefinition @event, TypeReference eventType, string extra) + private static void ThrowWrongHookType(PropertyDefinition syncVar, EventDefinition @event, TypeReference eventType, string extra) { throw new HookMethodException($"Hook Event for '{syncVar.Name}' is invalid '{eventType.FullName}', Error Type: {extra}", @event); } diff --git a/Assets/Mirage/Weaver/Weaver.cs b/Assets/Mirage/Weaver/Weaver.cs index ecf9b15b739..1e34a035b68 100644 --- a/Assets/Mirage/Weaver/Weaver.cs +++ b/Assets/Mirage/Weaver/Weaver.cs @@ -20,7 +20,6 @@ public class Weaver : WeaverBase { private Readers readers; private Writers writers; - private PropertySiteProcessor propertySiteProcessor; [Conditional("WEAVER_DEBUG_LOGS")] public static void DebugLog(TypeDefinition td, string message) @@ -43,7 +42,6 @@ protected override ResultType Process(AssemblyDefinition assembly, ICompiledAsse var module = assembly.MainModule; readers = new Readers(module, logger); writers = new Writers(module, logger); - propertySiteProcessor = new PropertySiteProcessor(); var rwProcessor = new ReaderWriterProcessor(module, readers, writers, logger); var modified = false; @@ -71,10 +69,7 @@ protected override ResultType Process(AssemblyDefinition assembly, ICompiledAsse if (modified) { - using (timer.Sample("propertySiteProcessor")) - { - propertySiteProcessor.Process(module); - } + using (timer.Sample("InitializeReaderAndWriters")) { @@ -154,7 +149,7 @@ private bool WeaveNetworkBehavior(FoundType foundType) var behaviour = behaviourClasses[i]; if (NetworkBehaviourProcessor.WasProcessed(behaviour)) { continue; } - modified |= new NetworkBehaviourProcessor(behaviour, readers, writers, propertySiteProcessor, logger).Process(); + modified |= new NetworkBehaviourProcessor(behaviour, readers, writers, logger).Process(); } return modified; } From 9a0eec2b41d0da027a6a39033200cd6350e9396a Mon Sep 17 00:00:00 2001 From: James Frowen Date: Sat, 30 May 2026 21:52:32 +0100 Subject: [PATCH 02/41] feat: restrict SyncVarAttribute usage to properties --- Assets/Mirage/Runtime/CustomAttributes.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Mirage/Runtime/CustomAttributes.cs b/Assets/Mirage/Runtime/CustomAttributes.cs index bfe41bf6daa..60f52fd63b0 100644 --- a/Assets/Mirage/Runtime/CustomAttributes.cs +++ b/Assets/Mirage/Runtime/CustomAttributes.cs @@ -7,7 +7,7 @@ namespace Mirage /// SyncVars are used to synchronize a variable from the server to all clients automatically. /// Value must be changed on server, not directly by clients. Hook parameter allows you to define a client-side method to be invoked when the client gets an update from the server. /// - [AttributeUsage(AttributeTargets.Field)] + [AttributeUsage(AttributeTargets.Property)] public class SyncVarAttribute : PropertyAttribute { ///A function that should be called on the client when the value changes. From 322ee06c7b8581146b7b9ccbc7e1150e2e3fb54a Mon Sep 17 00:00:00 2001 From: James Frowen Date: Sat, 30 May 2026 22:08:00 +0100 Subject: [PATCH 03/41] test: update generator templates --- .../.BitCountFromRangeTestTemplate.cs | 14 ++++++------ .../Tests/Generators/.BitCountTestTemplate.cs | 14 ++++++------ .../Generators/.FloatPackTestTemplate.cs | 18 +++++++-------- .../Generators/.QuaternionPackTestTemplate.cs | 18 +++++++-------- .../Generators/.VarIntBlocksTestTemplate.cs | 14 ++++++------ .../Tests/Generators/.VarIntTestTemplate.cs | 14 ++++++------ .../Generators/.Vector2PackTestTemplate.cs | 18 +++++++-------- .../Generators/.Vector3PackTestTemplate.cs | 22 +++++++++---------- .../Tests/Generators/.ZigzagTestTemplate.cs | 14 ++++++------ 9 files changed, 73 insertions(+), 73 deletions(-) diff --git a/Assets/Tests/Generators/.BitCountFromRangeTestTemplate.cs b/Assets/Tests/Generators/.BitCountFromRangeTestTemplate.cs index cd6ab6551ad..b19da9b675c 100644 --- a/Assets/Tests/Generators/.BitCountFromRangeTestTemplate.cs +++ b/Assets/Tests/Generators/.BitCountFromRangeTestTemplate.cs @@ -15,7 +15,7 @@ namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.%%NAME% public class BitPackBehaviour : NetworkBehaviour { [BitCountFromRange(%%MIN%%, %%MAX%%)] - [SyncVar] public %%TYPE%% myValue; + [SyncVar] public %%TYPE%% MyValue { get; set; } public event Action<%%TYPE%%> onRpc; @@ -37,14 +37,14 @@ public void RpcOtherFunction(BitPackStruct myParam) public struct BitPackMessage { [BitCountFromRange(%%MIN%%, %%MAX%%)] - public %%TYPE%% myValue; + public %%TYPE%% MyValue; } [Serializable] public struct BitPackStruct { [BitCountFromRange(%%MIN%%, %%MAX%%)] - public %%TYPE%% myValue; + public %%TYPE%% MyValue; } public class BitPackTest : ClientServerSetup @@ -54,7 +54,7 @@ public class BitPackTest : ClientServerSetup [Test] public void SyncVarIsBitPacked() { - serverComponent.myValue = value; + serverComponent.MyValue = value; using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { @@ -67,7 +67,7 @@ public void SyncVarIsBitPacked() clientComponent.DeserializeSyncVars(reader, true); Assert.That(reader.BitPosition, Is.EqualTo(%%BIT_COUNT%%)); - Assert.That(clientComponent.myValue, Is.EqualTo(value)); + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); } } } @@ -106,7 +106,7 @@ public IEnumerator StructIsBitPacked() { var inMessage = new BitPackMessage { - myValue = value, + MyValue = value, }; int payloadSize = 0; @@ -144,7 +144,7 @@ public void MessageIsBitPacked() { var inStruct = new BitPackStruct { - myValue = value, + MyValue = value, }; using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) diff --git a/Assets/Tests/Generators/.BitCountTestTemplate.cs b/Assets/Tests/Generators/.BitCountTestTemplate.cs index 56ea690843f..d81871aada3 100644 --- a/Assets/Tests/Generators/.BitCountTestTemplate.cs +++ b/Assets/Tests/Generators/.BitCountTestTemplate.cs @@ -15,7 +15,7 @@ namespace Mirage.Tests.Runtime.Generated.BitCountAttributeTests.%%TYPE%%_%%BIT_C public class BitPackBehaviour : NetworkBehaviour { [BitCount(%%BIT_COUNT%%)] - [SyncVar] public %%TYPE%% myValue; + [SyncVar] public %%TYPE%% MyValue { get; set; } public event Action<%%TYPE%%> onRpc; @@ -37,14 +37,14 @@ public void RpcOtherFunction(BitPackStruct myParam) public struct BitPackMessage { [BitCount(%%BIT_COUNT%%)] - public %%TYPE%% myValue; + public %%TYPE%% MyValue; } [Serializable] public struct BitPackStruct { [BitCount(%%BIT_COUNT%%)] - public %%TYPE%% myValue; + public %%TYPE%% MyValue; } public class BitPackTest : ClientServerSetup @@ -54,7 +54,7 @@ public class BitPackTest : ClientServerSetup [Test] public void SyncVarIsBitPacked() { - serverComponent.myValue = value; + serverComponent.MyValue = value; using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { @@ -67,7 +67,7 @@ public void SyncVarIsBitPacked() clientComponent.DeserializeSyncVars(reader, true); Assert.That(reader.BitPosition, Is.EqualTo(%%BIT_COUNT%%)); - Assert.That(clientComponent.myValue, Is.EqualTo(value)); + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); } } } @@ -106,7 +106,7 @@ public IEnumerator StructIsBitPacked() { var inMessage = new BitPackMessage { - myValue = value, + MyValue = value, }; int payloadSize = 0; @@ -144,7 +144,7 @@ public void MessageIsBitPacked() { var inStruct = new BitPackStruct { - myValue = value, + MyValue = value, }; using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) diff --git a/Assets/Tests/Generators/.FloatPackTestTemplate.cs b/Assets/Tests/Generators/.FloatPackTestTemplate.cs index b8f2d029893..7500ae4f93f 100644 --- a/Assets/Tests/Generators/.FloatPackTestTemplate.cs +++ b/Assets/Tests/Generators/.FloatPackTestTemplate.cs @@ -14,7 +14,7 @@ namespace Mirage.Tests.Runtime.Generated.FloatPackAttributeTests.%%NAME%% public class BitPackBehaviour : NetworkBehaviour { [FloatPack(%%PACKER_ATTRIBUTE%%)] - [SyncVar] public float myValue; + [SyncVar] public float MyValue { get; set; } public event Action onRpc; @@ -36,14 +36,14 @@ public void RpcOtherFunction(BitPackStruct myParam) public struct BitPackMessage { [FloatPack(%%PACKER_ATTRIBUTE%%)] - public float myValue; + public float MyValue; } [Serializable] public struct BitPackStruct { [FloatPack(%%PACKER_ATTRIBUTE%%)] - public float myValue; + public float MyValue; } public class BitPackTest : ClientServerSetup @@ -54,7 +54,7 @@ public class BitPackTest : ClientServerSetup [Test] public void SyncVarIsBitPacked() { - serverComponent.myValue = value; + serverComponent.MyValue = value; using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { @@ -67,7 +67,7 @@ public void SyncVarIsBitPacked() clientComponent.DeserializeSyncVars(reader, true); Assert.That(reader.BitPosition, Is.EqualTo(%%BIT_COUNT%%)); - Assert.That(clientComponent.myValue, Is.EqualTo(value).Within(within)); + Assert.That(clientComponent.MyValue, Is.EqualTo(value).Within(within)); } } } @@ -106,7 +106,7 @@ public IEnumerator StructIsBitPacked() { var inMessage = new BitPackMessage { - myValue = value, + MyValue = value, }; int payloadSize = 0; @@ -136,7 +136,7 @@ public IEnumerator StructIsBitPacked() // +2 for message header int expectedPayLoadSize = ((%%BIT_COUNT%% + 7) / 8) + 2; Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"%%BIT_COUNT%% bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage.myValue, Is.EqualTo(inMessage.myValue).Within(within)); + Assert.That(outMessage.MyValue, Is.EqualTo(inMessage.MyValue).Within(within)); } [Test] @@ -144,7 +144,7 @@ public void MessageIsBitPacked() { var inStruct = new BitPackStruct { - myValue = value, + MyValue = value, }; using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) @@ -159,7 +159,7 @@ public void MessageIsBitPacked() var outStruct = reader.Read(); Assert.That(reader.BitPosition, Is.EqualTo(%%BIT_COUNT%%)); - Assert.That(outStruct.myValue, Is.EqualTo(inStruct.myValue).Within(within)); + Assert.That(outStruct.MyValue, Is.EqualTo(inStruct.MyValue).Within(within)); } } } diff --git a/Assets/Tests/Generators/.QuaternionPackTestTemplate.cs b/Assets/Tests/Generators/.QuaternionPackTestTemplate.cs index 6b8ee59a91d..192df947616 100644 --- a/Assets/Tests/Generators/.QuaternionPackTestTemplate.cs +++ b/Assets/Tests/Generators/.QuaternionPackTestTemplate.cs @@ -14,7 +14,7 @@ namespace Mirage.Tests.Runtime.Generated.QuaternionPackAttributeTests.%%NAME%% public class BitPackBehaviour : NetworkBehaviour { [QuaternionPack(%%PACKER_ATTRIBUTE%%)] - [SyncVar] public Quaternion myValue; + [SyncVar] public Quaternion MyValue { get; set; } public event Action onRpc; @@ -36,14 +36,14 @@ public void RpcOtherFunction(BitPackStruct myParam) public struct BitPackMessage { [QuaternionPack(%%PACKER_ATTRIBUTE%%)] - public Quaternion myValue; + public Quaternion MyValue; } [Serializable] public struct BitPackStruct { [QuaternionPack(%%PACKER_ATTRIBUTE%%)] - public Quaternion myValue; + public Quaternion MyValue; } public class BitPackTest : ClientServerSetup @@ -65,7 +65,7 @@ private static void AssertValue(Quaternion actual) [Test] public void SyncVarIsBitPacked() { - serverComponent.myValue = value; + serverComponent.MyValue = value; using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { @@ -78,7 +78,7 @@ public void SyncVarIsBitPacked() clientComponent.DeserializeSyncVars(reader, true); Assert.That(reader.BitPosition, Is.EqualTo(%%BIT_COUNT%%)); - AssertValue(clientComponent.myValue); + AssertValue(clientComponent.MyValue); } } } @@ -117,7 +117,7 @@ public IEnumerator StructIsBitPacked() { var inMessage = new BitPackMessage { - myValue = value, + MyValue = value, }; int payloadSize = 0; @@ -147,7 +147,7 @@ public IEnumerator StructIsBitPacked() // +2 for message header int expectedPayLoadSize = ((%%BIT_COUNT%% + 7) / 8) + 2; Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"%%BIT_COUNT%% bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); + AssertValue(outMessage.MyValue); } [Test] @@ -155,7 +155,7 @@ public void MessageIsBitPacked() { var inStruct = new BitPackStruct { - myValue = value, + MyValue = value, }; using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) @@ -170,7 +170,7 @@ public void MessageIsBitPacked() var outStruct = reader.Read(); Assert.That(reader.BitPosition, Is.EqualTo(%%BIT_COUNT%%)); - AssertValue(outStruct.myValue); + AssertValue(outStruct.MyValue); } } } diff --git a/Assets/Tests/Generators/.VarIntBlocksTestTemplate.cs b/Assets/Tests/Generators/.VarIntBlocksTestTemplate.cs index 512c0fd11f7..dfeabe66024 100644 --- a/Assets/Tests/Generators/.VarIntBlocksTestTemplate.cs +++ b/Assets/Tests/Generators/.VarIntBlocksTestTemplate.cs @@ -15,7 +15,7 @@ namespace Mirage.Tests.Runtime.Generated.VarIntBlocksTests.%%NAME%% public class BitPackBehaviour : NetworkBehaviour { [VarIntBlocks(%%BLOCK_SIZE%%)] - [SyncVar] public %%TYPE%% myValue; + [SyncVar] public %%TYPE%% MyValue { get; set; } public event Action<%%TYPE%%> onRpc; @@ -37,14 +37,14 @@ public void RpcOtherFunction(BitPackStruct myParam) public struct BitPackMessage { [VarIntBlocks(%%BLOCK_SIZE%%)] - public %%TYPE%% myValue; + public %%TYPE%% MyValue; } [Serializable] public struct BitPackStruct { [VarIntBlocks(%%BLOCK_SIZE%%)] - public %%TYPE%% myValue; + public %%TYPE%% MyValue; } public class BitPackTest : ClientServerSetup @@ -67,7 +67,7 @@ public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) %%TYPE%% value = TestCase.value; int expectedBitCount = TestCase.expectedBits; - serverComponent.myValue = value; + serverComponent.MyValue = value; using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { @@ -80,7 +80,7 @@ public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) clientComponent.DeserializeSyncVars(reader, true); Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - Assert.That(clientComponent.myValue, Is.EqualTo(value)); + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); } } } @@ -125,7 +125,7 @@ public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestC var inMessage = new BitPackMessage { - myValue = value, + MyValue = value, }; int payloadSize = 0; @@ -166,7 +166,7 @@ public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) var inStruct = new BitPackStruct { - myValue = value, + MyValue = value, }; using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) diff --git a/Assets/Tests/Generators/.VarIntTestTemplate.cs b/Assets/Tests/Generators/.VarIntTestTemplate.cs index b89350c5f45..c7196c78260 100644 --- a/Assets/Tests/Generators/.VarIntTestTemplate.cs +++ b/Assets/Tests/Generators/.VarIntTestTemplate.cs @@ -15,7 +15,7 @@ namespace Mirage.Tests.Runtime.Generated.VarIntTests.%%NAME%% public class BitPackBehaviour : NetworkBehaviour { [VarInt(%%ARGS%%)] - [SyncVar] public %%TYPE%% myValue; + [SyncVar] public %%TYPE%% MyValue { get; set; } public event Action<%%TYPE%%> onRpc; @@ -37,14 +37,14 @@ public void RpcOtherFunction(BitPackStruct myParam) public struct BitPackMessage { [VarInt(%%ARGS%%)] - public %%TYPE%% myValue; + public %%TYPE%% MyValue; } [Serializable] public struct BitPackStruct { [VarInt(%%ARGS%%)] - public %%TYPE%% myValue; + public %%TYPE%% MyValue; } public class BitPackTest : ClientServerSetup @@ -67,7 +67,7 @@ public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) %%TYPE%% value = TestCase.value; int expectedBitCount = TestCase.expectedBits; - serverComponent.myValue = value; + serverComponent.MyValue = value; using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { @@ -80,7 +80,7 @@ public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) clientComponent.DeserializeSyncVars(reader, true); Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - Assert.That(clientComponent.myValue, Is.EqualTo(value)); + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); } } } @@ -125,7 +125,7 @@ public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestC var inMessage = new BitPackMessage { - myValue = value, + MyValue = value, }; int payloadSize = 0; @@ -166,7 +166,7 @@ public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) var inStruct = new BitPackStruct { - myValue = value, + MyValue = value, }; using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) diff --git a/Assets/Tests/Generators/.Vector2PackTestTemplate.cs b/Assets/Tests/Generators/.Vector2PackTestTemplate.cs index 52183f00786..f57fab6a0ba 100644 --- a/Assets/Tests/Generators/.Vector2PackTestTemplate.cs +++ b/Assets/Tests/Generators/.Vector2PackTestTemplate.cs @@ -14,7 +14,7 @@ namespace Mirage.Tests.Runtime.Generated.Vector2PackAttributeTests.%%NAME%% public class BitPackBehaviour : NetworkBehaviour { [Vector2Pack(%%PACKER_ATTRIBUTE%%)] - [SyncVar] public Vector2 myValue; + [SyncVar] public Vector2 MyValue { get; set; } public event Action onRpc; @@ -36,14 +36,14 @@ public void RpcOtherFunction(BitPackStruct myParam) public struct BitPackMessage { [Vector2Pack(%%PACKER_ATTRIBUTE%%)] - public Vector2 myValue; + public Vector2 MyValue; } [Serializable] public struct BitPackStruct { [Vector2Pack(%%PACKER_ATTRIBUTE%%)] - public Vector2 myValue; + public Vector2 MyValue; } public class BitPackTest : ClientServerSetup @@ -60,7 +60,7 @@ private static void AssertValue(Vector2 actual) [Test] public void SyncVarIsBitPacked() { - serverComponent.myValue = value; + serverComponent.MyValue = value; using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { @@ -73,7 +73,7 @@ public void SyncVarIsBitPacked() clientComponent.DeserializeSyncVars(reader, true); Assert.That(reader.BitPosition, Is.EqualTo(%%BIT_COUNT%%)); - AssertValue(clientComponent.myValue); + AssertValue(clientComponent.MyValue); } } } @@ -112,7 +112,7 @@ public IEnumerator StructIsBitPacked() { var inMessage = new BitPackMessage { - myValue = value, + MyValue = value, }; int payloadSize = 0; @@ -142,7 +142,7 @@ public IEnumerator StructIsBitPacked() // +2 for message header int expectedPayLoadSize = ((%%BIT_COUNT%% + 7) / 8) + 2; Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"%%BIT_COUNT%% bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); + AssertValue(outMessage.MyValue); } [Test] @@ -150,7 +150,7 @@ public void MessageIsBitPacked() { var inStruct = new BitPackStruct { - myValue = value, + MyValue = value, }; using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) @@ -165,7 +165,7 @@ public void MessageIsBitPacked() var outStruct = reader.Read(); Assert.That(reader.BitPosition, Is.EqualTo(%%BIT_COUNT%%)); - AssertValue(outStruct.myValue); + AssertValue(outStruct.MyValue); } } } diff --git a/Assets/Tests/Generators/.Vector3PackTestTemplate.cs b/Assets/Tests/Generators/.Vector3PackTestTemplate.cs index 88620e32599..9ad21334fec 100644 --- a/Assets/Tests/Generators/.Vector3PackTestTemplate.cs +++ b/Assets/Tests/Generators/.Vector3PackTestTemplate.cs @@ -14,7 +14,7 @@ namespace Mirage.Tests.Runtime.Generated.Vector3PackAttributeTests.%%NAME%% public class BitPackBehaviour : NetworkBehaviour { [Vector3Pack(%%PACKER_ATTRIBUTE%%)] - [SyncVar] public Vector3 myValue; + [SyncVar] public Vector3 MyValue { get; set; } public event Action onRpc; @@ -36,14 +36,14 @@ public void RpcOtherFunction(BitPackStruct myParam) public struct BitPackMessage { [Vector3Pack(%%PACKER_ATTRIBUTE%%)] - public Vector3 myValue; + public Vector3 MyValue; } [Serializable] public struct BitPackStruct { [Vector3Pack(%%PACKER_ATTRIBUTE%%)] - public Vector3 myValue; + public Vector3 MyValue; } public class BitPackTest : ClientServerSetup @@ -61,7 +61,7 @@ private static void AssertValue(Vector3 actual) [Test] public void SyncVarIsBitPacked() { - serverComponent.myValue = value; + serverComponent.MyValue = value; using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { @@ -74,9 +74,9 @@ public void SyncVarIsBitPacked() clientComponent.DeserializeSyncVars(reader, true); Assert.That(reader.BitPosition, Is.EqualTo(%%BIT_COUNT%%)); - Assert.That(clientComponent.myValue.x, Is.EqualTo(value.x).Within(within)); - Assert.That(clientComponent.myValue.y, Is.EqualTo(value.y).Within(within)); - Assert.That(clientComponent.myValue.z, Is.EqualTo(value.z).Within(within)); + Assert.That(clientComponent.MyValue.x, Is.EqualTo(value.x).Within(within)); + Assert.That(clientComponent.MyValue.y, Is.EqualTo(value.y).Within(within)); + Assert.That(clientComponent.MyValue.z, Is.EqualTo(value.z).Within(within)); } } } @@ -115,7 +115,7 @@ public IEnumerator StructIsBitPacked() { var inMessage = new BitPackMessage { - myValue = value, + MyValue = value, }; int payloadSize = 0; @@ -145,7 +145,7 @@ public IEnumerator StructIsBitPacked() // +2 for message header int expectedPayLoadSize = ((%%BIT_COUNT%% + 7) / 8) + 2; Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"%%BIT_COUNT%% bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); + AssertValue(outMessage.MyValue); } [Test] @@ -153,7 +153,7 @@ public void MessageIsBitPacked() { var inStruct = new BitPackStruct { - myValue = value, + MyValue = value, }; using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) @@ -168,7 +168,7 @@ public void MessageIsBitPacked() var outStruct = reader.Read(); Assert.That(reader.BitPosition, Is.EqualTo(%%BIT_COUNT%%)); - AssertValue(outStruct.myValue); + AssertValue(outStruct.MyValue); } } } diff --git a/Assets/Tests/Generators/.ZigzagTestTemplate.cs b/Assets/Tests/Generators/.ZigzagTestTemplate.cs index e396415899e..4e5e38c4b8a 100644 --- a/Assets/Tests/Generators/.ZigzagTestTemplate.cs +++ b/Assets/Tests/Generators/.ZigzagTestTemplate.cs @@ -15,7 +15,7 @@ namespace Mirage.Tests.Runtime.Generated.ZigZagAttributeTests.%%TYPE%%_%%BIT_COU public class BitPackBehaviour : NetworkBehaviour { [BitCount(%%BIT_COUNT%%), ZigZagEncode] - [SyncVar] public %%TYPE%% myValue; + [SyncVar] public %%TYPE%% MyValue { get; set; } public event Action<%%TYPE%%> onRpc; @@ -37,14 +37,14 @@ public void RpcOtherFunction(BitPackStruct myParam) public struct BitPackMessage { [BitCount(%%BIT_COUNT%%), ZigZagEncode] - public %%TYPE%% myValue; + public %%TYPE%% MyValue; } [Serializable] public struct BitPackStruct { [BitCount(%%BIT_COUNT%%), ZigZagEncode] - public %%TYPE%% myValue; + public %%TYPE%% MyValue; } public class BitPackTest : ClientServerSetup @@ -54,7 +54,7 @@ public class BitPackTest : ClientServerSetup [Test] public void SyncVarIsBitPacked() { - serverComponent.myValue = value; + serverComponent.MyValue = value; using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { @@ -67,7 +67,7 @@ public void SyncVarIsBitPacked() clientComponent.DeserializeSyncVars(reader, true); Assert.That(reader.BitPosition, Is.EqualTo(%%BIT_COUNT%%)); - Assert.That(clientComponent.myValue, Is.EqualTo(value)); + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); } } } @@ -106,7 +106,7 @@ public IEnumerator StructIsBitPacked() { var inMessage = new BitPackMessage { - myValue = value, + MyValue = value, }; int payloadSize = 0; @@ -144,7 +144,7 @@ public void MessageIsBitPacked() { var inStruct = new BitPackStruct { - myValue = value, + MyValue = value, }; using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) From 2e9cc908f7946b31ce0c2f4a98d94fdab11f7670 Mon Sep 17 00:00:00 2001 From: James Frowen Date: Sat, 30 May 2026 22:23:01 +0100 Subject: [PATCH 04/41] test: convert Weaver compiler tests and runtime tests to properties --- Assets/Tests/GUITest/SyncVarGuiTest.cs | 12 +- .../Runtime/ClientServer/EnumSyncVarTest.cs | 2 +- .../ClientServer/GameObjectSyncvarTest.cs | 2 +- .../Runtime/ClientServer/GenericBehaviours.cs | 10 +- .../GenericNetworkBehaviorSyncvarTest.cs | 16 +-- ...enericNetworkBehaviourSyncvarDeeperTest.cs | 24 ++-- ...ericNetworkBehaviourSyncvarNoMiddleTest.cs | 16 +-- .../Generics/GenericWithSyncVar.cs | 8 +- .../Generics/WithGenericInstances.cs | 6 +- .../Generics/WithGenericSyncVar.cs | 2 +- .../Generics/WithGenericSyncVarHookBig.cs | 8 +- .../NetworkBehaviorSyncvarTest.cs | 2 +- .../ClientServer/SyncVarInitialOnlyTest.cs | 6 +- Assets/Tests/Runtime/Syncing/SyncVarTest.cs | 4 +- .../Syncing/SyncVarWithBaseClassTest.cs | 20 +-- .../MonoBehaviourSyncVar.cs | 2 +- .../NormalClassSyncVar.cs | 2 +- .../Weaver/BitAttributeTests~/BitCount.cs | 20 +-- .../BitAttributeTests~/BitCountFromRange.cs | 14 +- .../BitCountFromRangeInvalid.cs | 16 +-- .../BitAttributeTests~/BitCountInvalid.cs | 26 ++-- .../Weaver/BitAttributeTests~/FloatPack.cs | 8 +- .../BitAttributeTests~/FloatPackInvalid.cs | 18 +-- .../BitAttributeTests~/QuaternionPack.cs | 4 +- .../QuaternionPackInvalid.cs | 8 +- .../Tests/Weaver/BitAttributeTests~/VarInt.cs | 20 +-- .../Weaver/BitAttributeTests~/VarIntBlocks.cs | 20 +-- .../BitAttributeTests~/VarIntBlocksInvalid.cs | 10 +- .../BitAttributeTests~/VarIntInvalid.cs | 26 ++-- .../Weaver/BitAttributeTests~/Vector2Pack.cs | 10 +- .../BitAttributeTests~/Vector2PackInvalid.cs | 18 +-- .../Weaver/BitAttributeTests~/Vector3Pack.cs | 10 +- .../BitAttributeTests~/Vector3PackInvalid.cs | 22 +-- .../Tests/Weaver/BitAttributeTests~/ZigZag.cs | 6 +- .../BitAttributeTests~/ZigZagInvalid.cs | 6 +- .../AutomaticFound2Methods.cs | 2 +- .../SyncVarHookTests~/AutomaticHookEvent1.cs | 2 +- .../SyncVarHookTests~/AutomaticHookEvent2.cs | 2 +- .../SyncVarHookTests~/AutomaticHookMethod1.cs | 2 +- .../SyncVarHookTests~/AutomaticHookMethod2.cs | 2 +- .../SyncVarHookTests~/AutomaticNotFound.cs | 2 +- .../ErrorForWrongTypeNewParameter.cs | 2 +- .../ErrorForWrongTypeOldParameter.cs | 2 +- .../ErrorWhenEventArgsAreWrong.cs | 2 +- .../ErrorWhenHookNotAction.cs | 2 +- .../SyncVarHookTests~/ErrorWhenNoHookFound.cs | 2 +- ...rorWhenNoHookWithCorrectParametersFound.cs | 2 +- .../SyncVarHookTests~/ExplicitEvent1Found.cs | 2 +- .../ExplicitEvent1NotFound.cs | 2 +- .../SyncVarHookTests~/ExplicitEvent2Found.cs | 2 +- .../ExplicitEvent2NotFound.cs | 2 +- .../SyncVarHookTests~/ExplicitMethod1Found.cs | 2 +- .../ExplicitMethod1FoundWithOverLoad.cs | 2 +- .../ExplicitMethod1NotFound.cs | 2 +- .../SyncVarHookTests~/ExplicitMethod2Found.cs | 2 +- .../ExplicitMethod2FoundWithOverLoad.cs | 2 +- .../ExplicitMethod2NotFound.cs | 2 +- .../SyncVarHookTests~/FindsHookEvent.cs | 2 +- .../FindsHookWithGameObject.cs | 2 +- .../FindsHookWithNetworkIdentity.cs | 2 +- .../FindsHookWithOtherOverloadsInOrder.cs | 2 +- ...ndsHookWithOtherOverloadsInReverseOrder.cs | 2 +- .../SyncVarHookTests~/FindsPrivateHook.cs | 2 +- .../SyncVarHookTests~/FindsPublicHook.cs | 2 +- .../SyncVarHookTests~/FindsStaticHook.cs | 4 +- .../SyncVarHookTests~/SuccessGenericAction.cs | 2 +- .../SyncVarHookTests~/SyncVarHookServer.cs | 2 +- .../SyncVarHookServerError.cs | 2 +- .../SyncVarTests~/SyncVarArraySegment.cs | 2 +- .../SyncVarTests~/SyncVarsCantBeArray.cs | 2 +- .../SyncVarsDerivedNetworkBehaviour.cs | 2 +- .../SyncVarTests~/SyncVarsDifferentModule.cs | 4 +- .../SyncVarTests~/SyncVarsGenericField.cs | 2 +- .../SyncVarTests~/SyncVarsGenericParam.cs | 2 +- .../Weaver/SyncVarTests~/SyncVarsInterface.cs | 2 +- .../SyncVarTests~/SyncVarsMoreThan63.cs | 128 +++++++++--------- .../Weaver/SyncVarTests~/SyncVarsStatic.cs | 2 +- .../Weaver/SyncVarTests~/SyncVarsSyncList.cs | 2 +- .../SyncVarTests~/SyncVarsUnityComponent.cs | 2 +- .../Weaver/SyncVarTests~/SyncVarsValid.cs | 126 ++++++++--------- .../SyncVarTests~/SyncVarsValidInitialOnly.cs | 4 +- 81 files changed, 376 insertions(+), 376 deletions(-) diff --git a/Assets/Tests/GUITest/SyncVarGuiTest.cs b/Assets/Tests/GUITest/SyncVarGuiTest.cs index ea17ce56dca..de1ed8dbbd4 100644 --- a/Assets/Tests/GUITest/SyncVarGuiTest.cs +++ b/Assets/Tests/GUITest/SyncVarGuiTest.cs @@ -1,14 +1,14 @@ -using UnityEngine; +using UnityEngine; namespace Mirage.Tests.GUiTests { public class SyncVarGuiTest : NetworkBehaviour { - [SyncVar] public int Number; - [SyncVar] public string Str; - [SyncVar] public GameObject Obj; - [SyncVar] public NetworkIdentity IdentityField; - [SyncVar] public NoSyncGuiTest BehaviovurField; + [SyncVar] public int Number { get; set; } + [SyncVar] public string Str { get; set; } + [SyncVar] public GameObject Obj { get; set; } + [SyncVar] public NetworkIdentity IdentityField { get; set; } + [SyncVar] public NoSyncGuiTest BehaviovurField { get; set; } } } diff --git a/Assets/Tests/Runtime/ClientServer/EnumSyncVarTest.cs b/Assets/Tests/Runtime/ClientServer/EnumSyncVarTest.cs index 8bf16c5070d..c7b94a1b3d0 100644 --- a/Assets/Tests/Runtime/ClientServer/EnumSyncVarTest.cs +++ b/Assets/Tests/Runtime/ClientServer/EnumSyncVarTest.cs @@ -7,7 +7,7 @@ public class SampleBehaviorWithEnum : NetworkBehaviour public const Colors DefaultValue = Colors.Red; [SyncVar] - public Colors myColor = DefaultValue; + public Colors myColor { get; set; } = DefaultValue; } public enum Colors diff --git a/Assets/Tests/Runtime/ClientServer/GameObjectSyncvarTest.cs b/Assets/Tests/Runtime/ClientServer/GameObjectSyncvarTest.cs index 827b73dcd82..e939a78c4cc 100644 --- a/Assets/Tests/Runtime/ClientServer/GameObjectSyncvarTest.cs +++ b/Assets/Tests/Runtime/ClientServer/GameObjectSyncvarTest.cs @@ -9,7 +9,7 @@ namespace Mirage.Tests.Runtime.ClientServer public class SampleBehaviorWithGO : NetworkBehaviour { [SyncVar] - public GameObject target; + public GameObject target { get; set; } } public class GameObjectSyncvarTest : ClientServerSetup diff --git a/Assets/Tests/Runtime/ClientServer/GenericBehaviours.cs b/Assets/Tests/Runtime/ClientServer/GenericBehaviours.cs index 5edc45a3ee7..96dadf6e4f0 100644 --- a/Assets/Tests/Runtime/ClientServer/GenericBehaviours.cs +++ b/Assets/Tests/Runtime/ClientServer/GenericBehaviours.cs @@ -9,7 +9,7 @@ namespace Mirage.Tests.Runtime.ClientServer.GenericBehaviours.NoMiddle { public class With3 : NetworkBehaviour { - [SyncVar] public int a; + [SyncVar] public int a { get; set; } } public class With2 : With3 @@ -18,7 +18,7 @@ public class With2 : With3 public class With0 : With2 { - [SyncVar] public float b; + [SyncVar] public float b { get; set; } } public class GenericNetworkBehaviorSyncvarNoMiddleTest : ClientServerSetup @@ -39,17 +39,17 @@ namespace Mirage.Tests.Runtime.ClientServer.GenericBehaviours.SyncVarMiddle { public class With3 : NetworkBehaviour { - [SyncVar] public int a; + [SyncVar] public int a { get; set; } } public class With2 : With3 { - [SyncVar] public int b; + [SyncVar] public int b { get; set; } } public class With0 : With2 { - [SyncVar] public float c; + [SyncVar] public float c { get; set; } } public class GenericNetworkBehaviorSyncvarNoMiddleTest : ClientServerSetup diff --git a/Assets/Tests/Runtime/ClientServer/GenericNetworkBehaviorSyncvarTest.cs b/Assets/Tests/Runtime/ClientServer/GenericNetworkBehaviorSyncvarTest.cs index 5ce19d8e55f..d8809e95903 100644 --- a/Assets/Tests/Runtime/ClientServer/GenericNetworkBehaviorSyncvarTest.cs +++ b/Assets/Tests/Runtime/ClientServer/GenericNetworkBehaviorSyncvarTest.cs @@ -9,13 +9,13 @@ namespace Mirage.Tests.Runtime.ClientServer public class GenericBehaviourWithSyncVar : NetworkBehaviour { [SyncVar] - public int baseValue = 0; + public int baseValue { get; set; } = 0; [SyncVar(hook = nameof(OnSyncedBaseValueWithHook))] - public int baseValueWithHook = 0; + public int baseValueWithHook { get; set; } = 0; [SyncVar] - public NetworkBehaviour target; + public NetworkBehaviour target { get; set; } [SyncVar] - public NetworkIdentity targetIdentity; + public NetworkIdentity targetIdentity { get; set; } public Action onBaseValueChanged; @@ -32,13 +32,13 @@ public void OnSyncedBaseValueWithHook(int oldValue, int newValue) public class GenericBehaviourWithSyncVarImplement : GenericBehaviourWithSyncVar { [SyncVar] - public int childValue = 0; + public int childValue { get; set; } = 0; [SyncVar(hook = nameof(OnSyncedChildValueWithHook))] - public int childValueWithHook = 0; + public int childValueWithHook { get; set; } = 0; [SyncVar] - public NetworkBehaviour childTarget; + public NetworkBehaviour childTarget { get; set; } [SyncVar] - public NetworkIdentity childIdentity; + public NetworkIdentity childIdentity { get; set; } public Action onChildValueChanged; diff --git a/Assets/Tests/Runtime/ClientServer/GenericNetworkBehaviourSyncvarDeeperTest.cs b/Assets/Tests/Runtime/ClientServer/GenericNetworkBehaviourSyncvarDeeperTest.cs index a1c929fcbd4..d6af1edc189 100644 --- a/Assets/Tests/Runtime/ClientServer/GenericNetworkBehaviourSyncvarDeeperTest.cs +++ b/Assets/Tests/Runtime/ClientServer/GenericNetworkBehaviourSyncvarDeeperTest.cs @@ -9,13 +9,13 @@ namespace Mirage.Tests.Runtime.ClientServer public class GenericBehaviourWithSyncVarDeeperBase : NetworkBehaviour { [SyncVar] - public int baseValue = 0; + public int baseValue { get; set; } = 0; [SyncVar(hook = nameof(OnSyncedBaseValueWithHook))] - public int baseValueWithHook = 0; + public int baseValueWithHook { get; set; } = 0; [SyncVar] - public NetworkBehaviour target; + public NetworkBehaviour target { get; set; } [SyncVar] - public NetworkIdentity targetIdentity; + public NetworkIdentity targetIdentity { get; set; } public Action onBaseValueChanged; @@ -32,13 +32,13 @@ public void OnSyncedBaseValueWithHook(int oldValue, int newValue) public class GenericBehaviourWithSyncVarDeeperMiddle : GenericBehaviourWithSyncVarDeeperBase { [SyncVar] - public int middleValue = 0; + public int middleValue { get; set; } = 0; [SyncVar(hook = nameof(OnSyncedMiddleValueWithHook))] - public int middleValueWithHook = 0; + public int middleValueWithHook { get; set; } = 0; [SyncVar] - public NetworkBehaviour middleTarget; + public NetworkBehaviour middleTarget { get; set; } [SyncVar] - public NetworkIdentity middleIdentity; + public NetworkIdentity middleIdentity { get; set; } public Action onMiddleValueChanged; @@ -55,13 +55,13 @@ public void OnSyncedMiddleValueWithHook(int oldValue, int newValue) public class GenericBehaviourWithSyncVarDeeperImplement : GenericBehaviourWithSyncVarDeeperMiddle { [SyncVar] - public int implementValue = 0; + public int implementValue { get; set; } = 0; [SyncVar(hook = nameof(OnSyncedImplementValueWithHook))] - public int implementValueWithHook = 0; + public int implementValueWithHook { get; set; } = 0; [SyncVar] - public NetworkBehaviour implementTarget; + public NetworkBehaviour implementTarget { get; set; } [SyncVar] - public NetworkIdentity implementIdentity; + public NetworkIdentity implementIdentity { get; set; } public Action onImplementValueChanged; diff --git a/Assets/Tests/Runtime/ClientServer/GenericNetworkBehaviourSyncvarNoMiddleTest.cs b/Assets/Tests/Runtime/ClientServer/GenericNetworkBehaviourSyncvarNoMiddleTest.cs index ec1f6a4ea97..e12c0bf69e3 100644 --- a/Assets/Tests/Runtime/ClientServer/GenericNetworkBehaviourSyncvarNoMiddleTest.cs +++ b/Assets/Tests/Runtime/ClientServer/GenericNetworkBehaviourSyncvarNoMiddleTest.cs @@ -9,13 +9,13 @@ namespace Mirage.Tests.Runtime.ClientServer public class GenericBehaviourWithSyncVarNoMiddleBase : NetworkBehaviour { [SyncVar] - public int baseValue = 0; + public int baseValue { get; set; } = 0; [SyncVar(hook = nameof(OnSyncedBaseValueWithHook))] - public int baseValueWithHook = 0; + public int baseValueWithHook { get; set; } = 0; [SyncVar] - public NetworkBehaviour target; + public NetworkBehaviour target { get; set; } [SyncVar] - public NetworkIdentity targetIdentity; + public NetworkIdentity targetIdentity { get; set; } public Action onBaseValueChanged; @@ -39,13 +39,13 @@ public class GenericBehaviourWithSyncVarNoMiddleMiddle : GenericBehaviourWith public class GenericBehaviourWithSyncVarNoMiddleImplement : GenericBehaviourWithSyncVarNoMiddleMiddle { [SyncVar] - public int implementValue = 0; + public int implementValue { get; set; } = 0; [SyncVar(hook = nameof(OnSyncedImplementValueWithHook))] - public int implementValueWithHook = 0; + public int implementValueWithHook { get; set; } = 0; [SyncVar] - public NetworkBehaviour implementTarget; + public NetworkBehaviour implementTarget { get; set; } [SyncVar] - public NetworkIdentity implementIdentity; + public NetworkIdentity implementIdentity { get; set; } public Action onImplementValueChanged; diff --git a/Assets/Tests/Runtime/ClientServer/Generics/GenericWithSyncVar.cs b/Assets/Tests/Runtime/ClientServer/Generics/GenericWithSyncVar.cs index b8e98beecec..e30914d6c23 100644 --- a/Assets/Tests/Runtime/ClientServer/Generics/GenericWithSyncVar.cs +++ b/Assets/Tests/Runtime/ClientServer/Generics/GenericWithSyncVar.cs @@ -7,21 +7,21 @@ namespace Mirage.Tests.Runtime.ClientServer.Generics // note, this can't be a behaviour by itself, it must have a child class in order to be used public class GenericWithSyncVarBase_behaviour : NetworkBehaviour { - [SyncVar] public int baseValue; + [SyncVar] public int baseValue { get; set; } } public class GenericWithSyncVar_behaviour : GenericWithSyncVarBase_behaviour { - [SyncVar] public int value; + [SyncVar] public int value { get; set; } } public class GenericWithSyncVar_behaviourInt : GenericWithSyncVar_behaviour { - [SyncVar] public int moreValue; + [SyncVar] public int moreValue { get; set; } } public class GenericWithSyncVar_behaviourObject : GenericWithSyncVar_behaviour { - [SyncVar] public int moreValue; + [SyncVar] public int moreValue { get; set; } } public class GenericWithSyncVarInt : ClientServerSetup diff --git a/Assets/Tests/Runtime/ClientServer/Generics/WithGenericInstances.cs b/Assets/Tests/Runtime/ClientServer/Generics/WithGenericInstances.cs index c50a24ad3c0..298cafb8280 100644 --- a/Assets/Tests/Runtime/ClientServer/Generics/WithGenericInstances.cs +++ b/Assets/Tests/Runtime/ClientServer/Generics/WithGenericInstances.cs @@ -25,15 +25,15 @@ public class WithGenericInstances_Behaviour : NetworkBehaviour public readonly SyncList> myList = new SyncList>(); [SyncVar] - public MyStruct myVar; + public MyStruct myVar { get; set; } [SyncVar(hook = nameof(syncHook))] - public MyStruct varWithHook; + public MyStruct varWithHook { get; set; } private void syncHook(MyStruct _old, MyStruct _new) => onSyncHook?.Invoke(_old.value, _new.value); [SyncVar(hook = nameof(syncEvent))] - public MyStruct varWithEvent; + public MyStruct varWithEvent { get; set; } [ClientRpc] public void MyRpc(MyStruct value) diff --git a/Assets/Tests/Runtime/ClientServer/Generics/WithGenericSyncVar.cs b/Assets/Tests/Runtime/ClientServer/Generics/WithGenericSyncVar.cs index 4b856eb4ce6..78c52a82a91 100644 --- a/Assets/Tests/Runtime/ClientServer/Generics/WithGenericSyncVar.cs +++ b/Assets/Tests/Runtime/ClientServer/Generics/WithGenericSyncVar.cs @@ -7,7 +7,7 @@ namespace Mirage.Tests.Runtime.ClientServer.Generics public class WithGenericSyncVar_behaviour : NetworkBehaviour { [SyncVar] - public T value; + public T value { get; set; } } public class WithGenericSyncVar_behaviourInt : WithGenericSyncVar_behaviour diff --git a/Assets/Tests/Runtime/ClientServer/Generics/WithGenericSyncVarHookBig.cs b/Assets/Tests/Runtime/ClientServer/Generics/WithGenericSyncVarHookBig.cs index 20a9332f029..1ae7b483a06 100644 --- a/Assets/Tests/Runtime/ClientServer/Generics/WithGenericSyncVarHookBig.cs +++ b/Assets/Tests/Runtime/ClientServer/Generics/WithGenericSyncVarHookBig.cs @@ -12,7 +12,7 @@ public class WithGenericSyncVarBig_behaviour : NetworkBehaviour public event Action hookMethod; [SyncVar(hook = nameof(onValueChanged))] - public T value1; + public T value1 { get; set; } private void onValueChanged(T oldValue, T newValue) { @@ -22,11 +22,11 @@ private void onValueChanged(T oldValue, T newValue) public event Action hookEvent; [SyncVar(hook = nameof(hookEvent))] - public T value2; + public T value2 { get; set; } [SyncVar] - public int value3; + public int value3 { get; set; } [SyncVar] - public T value4; + public T value4 { get; set; } public T valueNotVar; } diff --git a/Assets/Tests/Runtime/ClientServer/NetworkBehaviorSyncvarTest.cs b/Assets/Tests/Runtime/ClientServer/NetworkBehaviorSyncvarTest.cs index e50d029e305..e8d2eb000b8 100644 --- a/Assets/Tests/Runtime/ClientServer/NetworkBehaviorSyncvarTest.cs +++ b/Assets/Tests/Runtime/ClientServer/NetworkBehaviorSyncvarTest.cs @@ -11,7 +11,7 @@ namespace Mirage.Tests.Runtime.ClientServer public class SampleBehaviorWithNB : NetworkBehaviour { [SyncVar] - public SampleBehaviorWithNB target; + public SampleBehaviorWithNB target { get; set; } } public class NetworkBehaviorSyncvarTest : ClientServerSetup diff --git a/Assets/Tests/Runtime/ClientServer/SyncVarInitialOnlyTest.cs b/Assets/Tests/Runtime/ClientServer/SyncVarInitialOnlyTest.cs index fb406d0de77..5c02d6cfce0 100644 --- a/Assets/Tests/Runtime/ClientServer/SyncVarInitialOnlyTest.cs +++ b/Assets/Tests/Runtime/ClientServer/SyncVarInitialOnlyTest.cs @@ -9,13 +9,13 @@ namespace Mirage.Tests.Runtime.ClientServer public class SyncVarInitialOnly : NetworkBehaviour { [SyncVar(initialOnly = true)] - public int weaponIndex = 1; + public int weaponIndex { get; set; } = 1; [SyncVar] - public int health = 100; + public int health { get; set; } = 100; [SyncVar(initialOnly = true)] - public float otherValue = 13f; + public float otherValue { get; set; } = 13f; } public class SyncVarInitialOnlyTest : ClientServerSetup diff --git a/Assets/Tests/Runtime/Syncing/SyncVarTest.cs b/Assets/Tests/Runtime/Syncing/SyncVarTest.cs index 8a4c29ea4d4..b038552d392 100644 --- a/Assets/Tests/Runtime/Syncing/SyncVarTest.cs +++ b/Assets/Tests/Runtime/Syncing/SyncVarTest.cs @@ -18,10 +18,10 @@ public Guild(string name) } [SyncVar] - public Guild guild; + public Guild guild { get; set; } [SyncVar] - public NetworkIdentity target; + public NetworkIdentity target { get; set; } public event Action OnSerializeCalled; diff --git a/Assets/Tests/Runtime/Syncing/SyncVarWithBaseClassTest.cs b/Assets/Tests/Runtime/Syncing/SyncVarWithBaseClassTest.cs index e0238cff852..7fb3d42cf35 100644 --- a/Assets/Tests/Runtime/Syncing/SyncVarWithBaseClassTest.cs +++ b/Assets/Tests/Runtime/Syncing/SyncVarWithBaseClassTest.cs @@ -5,22 +5,22 @@ namespace Mirage.Tests.Runtime.Syncing.SyncVarWithBaseClass { public class A : NetworkBehaviour { - [SyncVar] public int i0; - [SyncVar] public int i1; - [SyncVar] public int i2; - [SyncVar] public int i3; + [SyncVar] public int i0 { get; set; } + [SyncVar] public int i1 { get; set; } + [SyncVar] public int i2 { get; set; } + [SyncVar] public int i3 { get; set; } } public class B : A { - [SyncVar] public int i4; - [SyncVar] public int i5; - [SyncVar] public int i6; + [SyncVar] public int i4 { get; set; } + [SyncVar] public int i5 { get; set; } + [SyncVar] public int i6 { get; set; } } public class C : B { - [SyncVar] public int i7; - [SyncVar] public int i8; - [SyncVar] public int i9; + [SyncVar] public int i7 { get; set; } + [SyncVar] public int i8 { get; set; } + [SyncVar] public int i9 { get; set; } } public class SyncVarWithBaseClassTest : TestBase diff --git a/Assets/Tests/Weaver/BadAttributeUseageTests~/MonoBehaviourSyncVar.cs b/Assets/Tests/Weaver/BadAttributeUseageTests~/MonoBehaviourSyncVar.cs index 0732ed5a1a1..2f8a520d92d 100644 --- a/Assets/Tests/Weaver/BadAttributeUseageTests~/MonoBehaviourSyncVar.cs +++ b/Assets/Tests/Weaver/BadAttributeUseageTests~/MonoBehaviourSyncVar.cs @@ -6,6 +6,6 @@ namespace BadAttributeUseageTests.MonoBehaviourSyncVar class MonoBehaviourSyncVar : MonoBehaviour { [SyncVar] - int potato; + int potato { get; set; } } } diff --git a/Assets/Tests/Weaver/BadAttributeUseageTests~/NormalClassSyncVar.cs b/Assets/Tests/Weaver/BadAttributeUseageTests~/NormalClassSyncVar.cs index fcfb95d6c53..6e0fad6cb25 100644 --- a/Assets/Tests/Weaver/BadAttributeUseageTests~/NormalClassSyncVar.cs +++ b/Assets/Tests/Weaver/BadAttributeUseageTests~/NormalClassSyncVar.cs @@ -6,6 +6,6 @@ namespace BadAttributeUseageTests.NormalClassSyncVar class NormalClassSyncVar { [SyncVar] - int potato; + int potato { get; set; } } } diff --git a/Assets/Tests/Weaver/BitAttributeTests~/BitCount.cs b/Assets/Tests/Weaver/BitAttributeTests~/BitCount.cs index 2ac84dfd0d6..1c5e57a1616 100644 --- a/Assets/Tests/Weaver/BitAttributeTests~/BitCount.cs +++ b/Assets/Tests/Weaver/BitAttributeTests~/BitCount.cs @@ -6,34 +6,34 @@ namespace BitAttributeTests.BitCount public class MyBehaviour : NetworkBehaviour { [BitCount(5)] - [SyncVar] public byte value1; + [SyncVar] public byte value1 { get; set; } [BitCount(10)] - [SyncVar] public short value2; + [SyncVar] public short value2 { get; set; } [BitCount(9)] - [SyncVar] public ushort value3; + [SyncVar] public ushort value3 { get; set; } [BitCount(20)] - [SyncVar] public int value4; + [SyncVar] public int value4 { get; set; } [BitCount(10)] - [SyncVar] public uint value5; + [SyncVar] public uint value5 { get; set; } [BitCount(48)] - [SyncVar] public long value6; + [SyncVar] public long value6 { get; set; } [BitCount(5)] - [SyncVar] public ulong value7; + [SyncVar] public ulong value7 { get; set; } [BitCount(3)] - [SyncVar] public MyByteEnum value8; + [SyncVar] public MyByteEnum value8 { get; set; } [BitCount(10)] - [SyncVar] public MyShortEnum value9; + [SyncVar] public MyShortEnum value9 { get; set; } [BitCount(5)] - [SyncVar] public MyIntEnum value10; + [SyncVar] public MyIntEnum value10 { get; set; } } public enum MyIntEnum diff --git a/Assets/Tests/Weaver/BitAttributeTests~/BitCountFromRange.cs b/Assets/Tests/Weaver/BitAttributeTests~/BitCountFromRange.cs index 631c8b6fed8..98acc9573f8 100644 --- a/Assets/Tests/Weaver/BitAttributeTests~/BitCountFromRange.cs +++ b/Assets/Tests/Weaver/BitAttributeTests~/BitCountFromRange.cs @@ -7,25 +7,25 @@ namespace BitAttributeTests.BitCountFromRange public class MyBehaviour : NetworkBehaviour { [BitCountFromRange(-100, 100)] - [SyncVar] public int value1; + [SyncVar] public int value1 { get; set; } [BitCountFromRange(100, 1000)] - [SyncVar] public int value2; + [SyncVar] public int value2 { get; set; } [BitCountFromRange(0, 1000)] - [SyncVar] public uint value3; + [SyncVar] public uint value3 { get; set; } [BitCountFromRange(0, 250)] - [SyncVar] public byte value4; + [SyncVar] public byte value4 { get; set; } [BitCountFromRange(-1, 1)] - [SyncVar] public MyDirection value5; + [SyncVar] public MyDirection value5 { get; set; } [BitCountFromRange(0, 1)] - [SyncVar] public MyShortEnum value6; + [SyncVar] public MyShortEnum value6 { get; set; } [BitCountFromRange(0, 6)] - [SyncVar] public System.DayOfWeek value7; + [SyncVar] public System.DayOfWeek value7 { get; set; } } public enum MyDirection { diff --git a/Assets/Tests/Weaver/BitAttributeTests~/BitCountFromRangeInvalid.cs b/Assets/Tests/Weaver/BitAttributeTests~/BitCountFromRangeInvalid.cs index bfc77dca0bd..2559fcbe01a 100644 --- a/Assets/Tests/Weaver/BitAttributeTests~/BitCountFromRangeInvalid.cs +++ b/Assets/Tests/Weaver/BitAttributeTests~/BitCountFromRangeInvalid.cs @@ -8,34 +8,34 @@ public class MyBehaviour : NetworkBehaviour { // error, max must be greater than min [BitCountFromRange(0, 0)] - [SyncVar] public int value1; + [SyncVar] public int value1 { get; set; } // error, max must be greater than min [BitCountFromRange(10, 2)] - [SyncVar] public int value2; + [SyncVar] public int value2 { get; set; } // error, cant be used with bitcount [BitCount(4), BitCountFromRange(0, 8)] - [SyncVar] public int value3; + [SyncVar] public int value3 { get; set; } // error, max is above type max [BitCountFromRange(0, 300)] - [SyncVar] public byte value4; + [SyncVar] public byte value4 { get; set; } // error, max is above type max [BitCountFromRange(0, int.MaxValue)] - [SyncVar] public short value5; + [SyncVar] public short value5 { get; set; } // error, min is below type max [BitCountFromRange(-50, 50)] - [SyncVar] public uint value6; + [SyncVar] public uint value6 { get; set; } // error, unsupported type [BitCountFromRange(-50, 50)] - [SyncVar] public UnityEngine.Vector3 value7; + [SyncVar] public UnityEngine.Vector3 value7 { get; set; } // error, unsupported type [BitCountFromRange(-50, 50)] - [SyncVar] public long value8; + [SyncVar] public long value8 { get; set; } } } \ No newline at end of file diff --git a/Assets/Tests/Weaver/BitAttributeTests~/BitCountInvalid.cs b/Assets/Tests/Weaver/BitAttributeTests~/BitCountInvalid.cs index 1e9cfc3c8bb..9d8aee814d3 100644 --- a/Assets/Tests/Weaver/BitAttributeTests~/BitCountInvalid.cs +++ b/Assets/Tests/Weaver/BitAttributeTests~/BitCountInvalid.cs @@ -7,43 +7,43 @@ namespace BitAttributeTests.BitCountInvalid public class MyBehaviour : NetworkBehaviour { [BitCount(9)] - [SyncVar] public byte value1; + [SyncVar] public byte value1 { get; set; } [BitCount(17)] - [SyncVar] public short value2; + [SyncVar] public short value2 { get; set; } [BitCount(17)] - [SyncVar] public ushort value3; + [SyncVar] public ushort value3 { get; set; } [BitCount(33)] - [SyncVar] public int value4; + [SyncVar] public int value4 { get; set; } [BitCount(33)] - [SyncVar] public uint value5; + [SyncVar] public uint value5 { get; set; } [BitCount(65)] - [SyncVar] public long value6; + [SyncVar] public long value6 { get; set; } [BitCount(65)] - [SyncVar] public ulong value7; + [SyncVar] public ulong value7 { get; set; } [BitCount(9)] - [SyncVar] public MyByteEnum value8; + [SyncVar] public MyByteEnum value8 { get; set; } [BitCount(17)] - [SyncVar] public MyShortEnum value9; + [SyncVar] public MyShortEnum value9 { get; set; } [BitCount(33)] - [SyncVar] public MyIntEnum value10; + [SyncVar] public MyIntEnum value10 { get; set; } [BitCount(20)] - [SyncVar] public UnityEngine.Vector3 value11; + [SyncVar] public UnityEngine.Vector3 value11 { get; set; } [BitCount(0)] - [SyncVar] public int value12; + [SyncVar] public int value12 { get; set; } [BitCount(-1)] - [SyncVar] public int value13; + [SyncVar] public int value13 { get; set; } } public enum MyIntEnum diff --git a/Assets/Tests/Weaver/BitAttributeTests~/FloatPack.cs b/Assets/Tests/Weaver/BitAttributeTests~/FloatPack.cs index 2eadf517538..e124607c351 100644 --- a/Assets/Tests/Weaver/BitAttributeTests~/FloatPack.cs +++ b/Assets/Tests/Weaver/BitAttributeTests~/FloatPack.cs @@ -6,15 +6,15 @@ namespace BitAttributeTests.FloatPack public class MyBehaviour : NetworkBehaviour { [FloatPack(100f, 0.1f)] - [SyncVar] public float value1; + [SyncVar] public float value1 { get; set; } [FloatPack(1000f, 1f)] - [SyncVar] public float value2; + [SyncVar] public float value2 { get; set; } [FloatPack(10000f, 8)] - [SyncVar] public float value3; + [SyncVar] public float value3 { get; set; } [FloatPack(1f, 9)] - [SyncVar] public float value4; + [SyncVar] public float value4 { get; set; } } } \ No newline at end of file diff --git a/Assets/Tests/Weaver/BitAttributeTests~/FloatPackInvalid.cs b/Assets/Tests/Weaver/BitAttributeTests~/FloatPackInvalid.cs index b378aca4263..98aea999859 100644 --- a/Assets/Tests/Weaver/BitAttributeTests~/FloatPackInvalid.cs +++ b/Assets/Tests/Weaver/BitAttributeTests~/FloatPackInvalid.cs @@ -7,38 +7,38 @@ public class MyBehaviour : NetworkBehaviour { // error, unsupported type [FloatPack(100f, 0.1f)] - [SyncVar] public double value1; + [SyncVar] public double value1 { get; set; } // error, unsupported type [FloatPack(1000f, 1f)] - [SyncVar] public int value2; + [SyncVar] public int value2 { get; set; } // error, unsupported type [FloatPack(10000f, 8)] - [SyncVar] public UnityEngine.Vector3 value3; + [SyncVar] public UnityEngine.Vector3 value3 { get; set; } // error, bit count out of range [FloatPack(1f, 31)] - [SyncVar] public float value4; + [SyncVar] public float value4 { get; set; } // error, bit count out of range [FloatPack(1f, 0)] - [SyncVar] public float value5; + [SyncVar] public float value5 { get; set; } // error, max can't be zero [FloatPack(0, 10)] - [SyncVar] public float value6; + [SyncVar] public float value6 { get; set; } // error, max can't be zero [FloatPack(-5, 10)] - [SyncVar] public float value7; + [SyncVar] public float value7 { get; set; } // error, precision too low [FloatPack(1f, float.Epsilon)] - [SyncVar] public float value8; + [SyncVar] public float value8 { get; set; } // error, precision negative [FloatPack(1f, -0.1f)] - [SyncVar] public float value9; + [SyncVar] public float value9 { get; set; } } } \ No newline at end of file diff --git a/Assets/Tests/Weaver/BitAttributeTests~/QuaternionPack.cs b/Assets/Tests/Weaver/BitAttributeTests~/QuaternionPack.cs index 23da1fecc20..d08eb07989e 100644 --- a/Assets/Tests/Weaver/BitAttributeTests~/QuaternionPack.cs +++ b/Assets/Tests/Weaver/BitAttributeTests~/QuaternionPack.cs @@ -7,9 +7,9 @@ namespace BitAttributeTests.QuaternionPack public class MyBehaviour : NetworkBehaviour { [QuaternionPack(9)] - [SyncVar] public Quaternion value1; + [SyncVar] public Quaternion value1 { get; set; } [QuaternionPack(10)] - [SyncVar] public Quaternion value3; + [SyncVar] public Quaternion value3 { get; set; } } } \ No newline at end of file diff --git a/Assets/Tests/Weaver/BitAttributeTests~/QuaternionPackInvalid.cs b/Assets/Tests/Weaver/BitAttributeTests~/QuaternionPackInvalid.cs index 54d3b6ed3e7..ea0276bd2ae 100644 --- a/Assets/Tests/Weaver/BitAttributeTests~/QuaternionPackInvalid.cs +++ b/Assets/Tests/Weaver/BitAttributeTests~/QuaternionPackInvalid.cs @@ -8,18 +8,18 @@ public class MyBehaviour : NetworkBehaviour { // error, invalid type [QuaternionPack(9)] - [SyncVar] public float value1; + [SyncVar] public float value1 { get; set; } // error, invalid type [QuaternionPack(9)] - [SyncVar] public Vector3 value2; + [SyncVar] public Vector3 value2 { get; set; } // error, should be above 0 [QuaternionPack(0)] - [SyncVar] public Quaternion value3; + [SyncVar] public Quaternion value3 { get; set; } // error, should be below 20 [QuaternionPack(21)] - [SyncVar] public Quaternion value4; + [SyncVar] public Quaternion value4 { get; set; } } } \ No newline at end of file diff --git a/Assets/Tests/Weaver/BitAttributeTests~/VarInt.cs b/Assets/Tests/Weaver/BitAttributeTests~/VarInt.cs index e1669d2f262..91bf8c5ea7a 100644 --- a/Assets/Tests/Weaver/BitAttributeTests~/VarInt.cs +++ b/Assets/Tests/Weaver/BitAttributeTests~/VarInt.cs @@ -6,34 +6,34 @@ namespace BitAttributeTests.VarInt public class MyBehaviour : NetworkBehaviour { [VarInt(10, 100)] - [SyncVar] public byte value1; + [SyncVar] public byte value1 { get; set; } [VarInt(10, 500)] - [SyncVar] public short value2; + [SyncVar] public short value2 { get; set; } [VarInt(10, 500)] - [SyncVar] public ushort value3; + [SyncVar] public ushort value3 { get; set; } [VarInt(10, 500)] - [SyncVar] public int value4; + [SyncVar] public int value4 { get; set; } [VarInt(10, 500, 10000)] - [SyncVar] public uint value5; + [SyncVar] public uint value5 { get; set; } [VarInt(10, 500, 10000, false)] - [SyncVar] public long value6; + [SyncVar] public long value6 { get; set; } [VarInt(10, 500, 10000, false)] - [SyncVar] public ulong value7; + [SyncVar] public ulong value7 { get; set; } [VarInt(3, 10, 60)] - [SyncVar] public MyByteEnum value8; + [SyncVar] public MyByteEnum value8 { get; set; } [VarInt(3, 10, 60)] - [SyncVar] public MyShortEnum value9; + [SyncVar] public MyShortEnum value9 { get; set; } [VarInt(3, 10, 60)] - [SyncVar] public MyIntEnum value10; + [SyncVar] public MyIntEnum value10 { get; set; } } public enum MyIntEnum diff --git a/Assets/Tests/Weaver/BitAttributeTests~/VarIntBlocks.cs b/Assets/Tests/Weaver/BitAttributeTests~/VarIntBlocks.cs index b2e9c2238cf..c7f02e113ee 100644 --- a/Assets/Tests/Weaver/BitAttributeTests~/VarIntBlocks.cs +++ b/Assets/Tests/Weaver/BitAttributeTests~/VarIntBlocks.cs @@ -6,34 +6,34 @@ namespace BitAttributeTests.VarIntBlocks public class MyBehaviour : NetworkBehaviour { [VarIntBlocks(4)] - [SyncVar] public byte value1; + [SyncVar] public byte value1 { get; set; } [VarIntBlocks(6)] - [SyncVar] public short value2; + [SyncVar] public short value2 { get; set; } [VarIntBlocks(6)] - [SyncVar] public ushort value3; + [SyncVar] public ushort value3 { get; set; } [VarIntBlocks(8)] - [SyncVar] public int value4; + [SyncVar] public int value4 { get; set; } [VarIntBlocks(8)] - [SyncVar] public uint value5; + [SyncVar] public uint value5 { get; set; } [VarIntBlocks(10)] - [SyncVar] public long value6; + [SyncVar] public long value6 { get; set; } [VarIntBlocks(10)] - [SyncVar] public ulong value7; + [SyncVar] public ulong value7 { get; set; } [VarIntBlocks(2)] - [SyncVar] public MyByteEnum value8; + [SyncVar] public MyByteEnum value8 { get; set; } [VarIntBlocks(3)] - [SyncVar] public MyShortEnum value9; + [SyncVar] public MyShortEnum value9 { get; set; } [VarIntBlocks(4)] - [SyncVar] public MyIntEnum value10; + [SyncVar] public MyIntEnum value10 { get; set; } } public enum MyIntEnum diff --git a/Assets/Tests/Weaver/BitAttributeTests~/VarIntBlocksInvalid.cs b/Assets/Tests/Weaver/BitAttributeTests~/VarIntBlocksInvalid.cs index ad0d3537154..f04dba290f0 100644 --- a/Assets/Tests/Weaver/BitAttributeTests~/VarIntBlocksInvalid.cs +++ b/Assets/Tests/Weaver/BitAttributeTests~/VarIntBlocksInvalid.cs @@ -6,18 +6,18 @@ namespace BitAttributeTests.VarIntBlocksInValid public class MyBehaviour : NetworkBehaviour { [VarIntBlocks(0)] - [SyncVar] public int value1; + [SyncVar] public int value1 { get; set; } [VarIntBlocks(-2)] - [SyncVar] public int value2; + [SyncVar] public int value2 { get; set; } [VarIntBlocks(33)] - [SyncVar] public int value3; + [SyncVar] public int value3 { get; set; } [VarIntBlocks(6)] - [SyncVar] public float value4; + [SyncVar] public float value4 { get; set; } [VarIntBlocks(8)] - [SyncVar] public UnityEngine.Vector3 value5; + [SyncVar] public UnityEngine.Vector3 value5 { get; set; } } } \ No newline at end of file diff --git a/Assets/Tests/Weaver/BitAttributeTests~/VarIntInvalid.cs b/Assets/Tests/Weaver/BitAttributeTests~/VarIntInvalid.cs index f64d8962d7c..996bdbbd471 100644 --- a/Assets/Tests/Weaver/BitAttributeTests~/VarIntInvalid.cs +++ b/Assets/Tests/Weaver/BitAttributeTests~/VarIntInvalid.cs @@ -6,42 +6,42 @@ namespace BitAttributeTests.VarIntInvalid public class MyBehaviour : NetworkBehaviour { [VarInt(10, 100)] - [SyncVar] public float value1; + [SyncVar] public float value1 { get; set; } [VarInt(10, 100)] - [SyncVar] public UnityEngine.Vector3 value2; + [SyncVar] public UnityEngine.Vector3 value2 { get; set; } [VarInt(0, 100)] - [SyncVar] public int value3; + [SyncVar] public int value3 { get; set; } [VarInt(100, 0)] - [SyncVar] public int value4; + [SyncVar] public int value4 { get; set; } [VarInt(10, 100, 0)] - [SyncVar] public int value5; + [SyncVar] public int value5 { get; set; } [VarInt(200, 100)] - [SyncVar] public int value6; + [SyncVar] public int value6 { get; set; } [VarInt(50, 100, 65)] - [SyncVar] public int value7; + [SyncVar] public int value7 { get; set; } [VarInt(50, 60)] - [SyncVar] public int value8; + [SyncVar] public int value8 { get; set; } [VarInt(500, 1000)] - [SyncVar] public byte value9; + [SyncVar] public byte value9 { get; set; } [VarInt(100, 1000)] - [SyncVar] public byte value10; + [SyncVar] public byte value10 { get; set; } [VarInt(50, 100, 1000)] - [SyncVar] public byte value11; + [SyncVar] public byte value11 { get; set; } [BitCount(1), VarInt(50, 100, 1000)] - [SyncVar] public int value12; + [SyncVar] public int value12 { get; set; } [BitCountFromRange(-100, 100), VarInt(50, 100, 1000)] - [SyncVar] public int value13; + [SyncVar] public int value13 { get; set; } } } \ No newline at end of file diff --git a/Assets/Tests/Weaver/BitAttributeTests~/Vector2Pack.cs b/Assets/Tests/Weaver/BitAttributeTests~/Vector2Pack.cs index 3552600ce9b..70a8dcc33e0 100644 --- a/Assets/Tests/Weaver/BitAttributeTests~/Vector2Pack.cs +++ b/Assets/Tests/Weaver/BitAttributeTests~/Vector2Pack.cs @@ -7,18 +7,18 @@ namespace BitAttributeTests.Vector2Pack public class MyBehaviour : NetworkBehaviour { [Vector2Pack(100, 20, 0.1f, 0.1f)] - [SyncVar] public Vector2 value1; + [SyncVar] public Vector2 value1 { get; set; } [Vector2Pack(100, 20, 0.1f)] - [SyncVar] public Vector2 value2; + [SyncVar] public Vector2 value2 { get; set; } [Vector2Pack(100, 20, 10, 8)] - [SyncVar] public Vector2 value3; + [SyncVar] public Vector2 value3 { get; set; } [Vector2Pack(100, 20, 10)] - [SyncVar] public Vector2 value4; + [SyncVar] public Vector2 value4 { get; set; } [Vector2Pack(100, 100, 10)] - [SyncVar] public Vector2 value5; + [SyncVar] public Vector2 value5 { get; set; } } } \ No newline at end of file diff --git a/Assets/Tests/Weaver/BitAttributeTests~/Vector2PackInvalid.cs b/Assets/Tests/Weaver/BitAttributeTests~/Vector2PackInvalid.cs index 591e4db1d14..e7de72474a7 100644 --- a/Assets/Tests/Weaver/BitAttributeTests~/Vector2PackInvalid.cs +++ b/Assets/Tests/Weaver/BitAttributeTests~/Vector2PackInvalid.cs @@ -8,38 +8,38 @@ public class MyBehaviour : NetworkBehaviour { // error, invalid type [Vector2Pack(1f, 1f, 10, 10)] - [SyncVar] public float value1; + [SyncVar] public float value1 { get; set; } // error, invalid type [Vector2Pack(1f, 1f, 10, 10)] - [SyncVar] public Vector3 value2; + [SyncVar] public Vector3 value2 { get; set; } // error, bit count out of range [Vector2Pack(1f, 1f, 31, 31)] - [SyncVar] public Vector2 value3; + [SyncVar] public Vector2 value3 { get; set; } // error, bit count out of range [Vector2Pack(1f, 1f, 31)] - [SyncVar] public Vector2 value4; + [SyncVar] public Vector2 value4 { get; set; } // error, bit count out of range [Vector2Pack(1f, 1f, 0, 10)] - [SyncVar] public Vector2 value5; + [SyncVar] public Vector2 value5 { get; set; } // error, bit count out of range [Vector2Pack(1f, 1f, 10, 0)] - [SyncVar] public Vector2 value6; + [SyncVar] public Vector2 value6 { get; set; } // error, bit count out of range [Vector2Pack(-1f, 1f, 10)] - [SyncVar] public Vector2 value7; + [SyncVar] public Vector2 value7 { get; set; } // error, bit count out of range [Vector2Pack(1f, -1f, 10)] - [SyncVar] public Vector2 value8; + [SyncVar] public Vector2 value8 { get; set; } // error, bit count out of range [Vector2Pack(-1f, -1f, 10, 10)] - [SyncVar] public Vector2 value9; + [SyncVar] public Vector2 value9 { get; set; } } } \ No newline at end of file diff --git a/Assets/Tests/Weaver/BitAttributeTests~/Vector3Pack.cs b/Assets/Tests/Weaver/BitAttributeTests~/Vector3Pack.cs index 2abd51efae3..2329e4b8078 100644 --- a/Assets/Tests/Weaver/BitAttributeTests~/Vector3Pack.cs +++ b/Assets/Tests/Weaver/BitAttributeTests~/Vector3Pack.cs @@ -7,18 +7,18 @@ namespace BitAttributeTests.Vector3Pack public class MyBehaviour : NetworkBehaviour { [Vector3Pack(100, 20, 80, 0.1f, 0.05f, 0.1f)] - [SyncVar] public Vector3 value1; + [SyncVar] public Vector3 value1 { get; set; } [Vector3Pack(100, 20, 80, 0.1f)] - [SyncVar] public Vector3 value2; + [SyncVar] public Vector3 value2 { get; set; } [Vector3Pack(100, 20, 80, 10, 8, 10)] - [SyncVar] public Vector3 value3; + [SyncVar] public Vector3 value3 { get; set; } [Vector3Pack(100, 20, 80, 10)] - [SyncVar] public Vector3 value4; + [SyncVar] public Vector3 value4 { get; set; } [Vector3Pack(100, 100, 100, 10)] - [SyncVar] public Vector3 value5; + [SyncVar] public Vector3 value5 { get; set; } } } \ No newline at end of file diff --git a/Assets/Tests/Weaver/BitAttributeTests~/Vector3PackInvalid.cs b/Assets/Tests/Weaver/BitAttributeTests~/Vector3PackInvalid.cs index 44aac7be0aa..9f382bd3283 100644 --- a/Assets/Tests/Weaver/BitAttributeTests~/Vector3PackInvalid.cs +++ b/Assets/Tests/Weaver/BitAttributeTests~/Vector3PackInvalid.cs @@ -8,46 +8,46 @@ public class MyBehaviour : NetworkBehaviour { // error, invalid type [Vector3Pack(1f, 1f, 1f, 10)] - [SyncVar] public float value1; + [SyncVar] public float value1 { get; set; } // error, bit count out of range [Vector3Pack(1f, 1f, 1f, 10)] - [SyncVar] public Vector2 value2; + [SyncVar] public Vector2 value2 { get; set; } // error, bit count out of range [Vector3Pack(1f, 1f, 1f, 31, 31, 31)] - [SyncVar] public Vector3 value3; + [SyncVar] public Vector3 value3 { get; set; } // error, bit count out of range [Vector3Pack(1f, 1f, 1f, 31)] - [SyncVar] public Vector3 value4; + [SyncVar] public Vector3 value4 { get; set; } // error, bit count out of range [Vector3Pack(1f, 1f, 1f, 0, 10, 10)] - [SyncVar] public Vector3 value5; + [SyncVar] public Vector3 value5 { get; set; } // error, bit count out of range [Vector3Pack(1f, 1f, 1f, 10, 0, 10)] - [SyncVar] public Vector3 value6; + [SyncVar] public Vector3 value6 { get; set; } // error, bit count out of range [Vector3Pack(1f, 1f, 1f, 10, 10, 0)] - [SyncVar] public Vector3 value7; + [SyncVar] public Vector3 value7 { get; set; } // error, bit count out of range [Vector3Pack(-1f, 1f, 1f, 10)] - [SyncVar] public Vector3 value8; + [SyncVar] public Vector3 value8 { get; set; } // error, bit count out of range [Vector3Pack(1f, -1f, 1f, 10)] - [SyncVar] public Vector3 value9; + [SyncVar] public Vector3 value9 { get; set; } // error, bit count out of range [Vector3Pack(1f, 1f, -1f, 10)] - [SyncVar] public Vector3 value10; + [SyncVar] public Vector3 value10 { get; set; } // error, bit count out of range [Vector3Pack(-1f, -1f, 1f, 10, 10, 10)] - [SyncVar] public Vector3 value11; + [SyncVar] public Vector3 value11 { get; set; } } } \ No newline at end of file diff --git a/Assets/Tests/Weaver/BitAttributeTests~/ZigZag.cs b/Assets/Tests/Weaver/BitAttributeTests~/ZigZag.cs index 856f1a5be73..51229c40c21 100644 --- a/Assets/Tests/Weaver/BitAttributeTests~/ZigZag.cs +++ b/Assets/Tests/Weaver/BitAttributeTests~/ZigZag.cs @@ -6,13 +6,13 @@ namespace BitAttributeTests.ZigZag public class MyBehaviour : NetworkBehaviour { [BitCount(10), ZigZagEncode] - [SyncVar] public int value1; + [SyncVar] public int value1 { get; set; } [BitCount(4), ZigZagEncode] - [SyncVar] public MyIntEnum value2; + [SyncVar] public MyIntEnum value2 { get; set; } [BitCount(4), ZigZagEncode] - [SyncVar] public MyShortEnum value3; + [SyncVar] public MyShortEnum value3 { get; set; } } public enum MyIntEnum diff --git a/Assets/Tests/Weaver/BitAttributeTests~/ZigZagInvalid.cs b/Assets/Tests/Weaver/BitAttributeTests~/ZigZagInvalid.cs index b078662bf72..a0f6adfd4c3 100644 --- a/Assets/Tests/Weaver/BitAttributeTests~/ZigZagInvalid.cs +++ b/Assets/Tests/Weaver/BitAttributeTests~/ZigZagInvalid.cs @@ -8,15 +8,15 @@ public class MyBehaviour : NetworkBehaviour { // error, ZigZag can't be used by itself [ZigZagEncode] - [SyncVar] public int value1; + [SyncVar] public int value1 { get; set; } // error, ZigZag should be used on signed fields [BitCount(8), ZigZagEncode] - [SyncVar] public uint value2; + [SyncVar] public uint value2 { get; set; } // error, ZigZag should be used on signed fields [BitCount(8), ZigZagEncode] - [SyncVar] public MyShortEnum value3; + [SyncVar] public MyShortEnum value3 { get; set; } } public enum MyShortEnum : ushort diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/AutomaticFound2Methods.cs b/Assets/Tests/Weaver/SyncVarHookTests~/AutomaticFound2Methods.cs index f04f88c0ab7..01afb5c52c4 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/AutomaticFound2Methods.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/AutomaticFound2Methods.cs @@ -5,7 +5,7 @@ namespace SyncVarHookTests.AutomaticFound2Methods class AutomaticFound2Methods : NetworkBehaviour { [SyncVar(hook = nameof(onChangeHealth))] - int health; + int health { get; set; } public void onChangeHealth(int oldValue, int newValue) { diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/AutomaticHookEvent1.cs b/Assets/Tests/Weaver/SyncVarHookTests~/AutomaticHookEvent1.cs index db00b3b25cd..f697b813db2 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/AutomaticHookEvent1.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/AutomaticHookEvent1.cs @@ -6,7 +6,7 @@ namespace SyncVarHookTests.AutomaticHookEvent1 class AutomaticHookEvent1 : NetworkBehaviour { [SyncVar(hook = nameof(onChangeHealth))] - int health; + int health { get; set; } event Action onChangeHealth; } diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/AutomaticHookEvent2.cs b/Assets/Tests/Weaver/SyncVarHookTests~/AutomaticHookEvent2.cs index 0c75e6f8dfd..b61418906a3 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/AutomaticHookEvent2.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/AutomaticHookEvent2.cs @@ -6,7 +6,7 @@ namespace SyncVarHookTests.AutomaticHookEvent2 class AutomaticHookEvent2 : NetworkBehaviour { [SyncVar(hook = nameof(onChangeHealth))] - int health; + int health { get; set; } event Action onChangeHealth; } diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/AutomaticHookMethod1.cs b/Assets/Tests/Weaver/SyncVarHookTests~/AutomaticHookMethod1.cs index 2cee1573d7d..072d320f2d4 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/AutomaticHookMethod1.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/AutomaticHookMethod1.cs @@ -5,7 +5,7 @@ namespace SyncVarHookTests.AutomaticHookMethod1 class AutomaticHookMethod1 : NetworkBehaviour { [SyncVar(hook = nameof(onChangeHealth))] - int health; + int health { get; set; } public void onChangeHealth(int newValue) { diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/AutomaticHookMethod2.cs b/Assets/Tests/Weaver/SyncVarHookTests~/AutomaticHookMethod2.cs index 062368d1fe6..b718461b794 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/AutomaticHookMethod2.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/AutomaticHookMethod2.cs @@ -5,7 +5,7 @@ namespace SyncVarHookTests.AutomaticHookMethod2 class AutomaticHookMethod2 : NetworkBehaviour { [SyncVar(hook = nameof(onChangeHealth))] - int health; + int health { get; set; } public void onChangeHealth(int oldValue, int newValue) { diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/AutomaticNotFound.cs b/Assets/Tests/Weaver/SyncVarHookTests~/AutomaticNotFound.cs index 10c8ba7d593..27881fa232f 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/AutomaticNotFound.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/AutomaticNotFound.cs @@ -5,6 +5,6 @@ namespace SyncVarHookTests.AutomaticNotFound class AutomaticNotFound : NetworkBehaviour { [SyncVar(hook = "onChangeHealth")] - int health; + int health { get; set; } } } diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/ErrorForWrongTypeNewParameter.cs b/Assets/Tests/Weaver/SyncVarHookTests~/ErrorForWrongTypeNewParameter.cs index 3689a0941e2..c5efbc2a618 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/ErrorForWrongTypeNewParameter.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/ErrorForWrongTypeNewParameter.cs @@ -5,7 +5,7 @@ namespace SyncVarHookTests.ErrorForWrongTypeNewParameter class ErrorForWrongTypeNewParameter : NetworkBehaviour { [SyncVar(hook = nameof(onChangeHealth))] - int health; + int health { get; set; } void onChangeHealth(int oldValue, float wrongNewValue) { diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/ErrorForWrongTypeOldParameter.cs b/Assets/Tests/Weaver/SyncVarHookTests~/ErrorForWrongTypeOldParameter.cs index 4402d8353af..dc540e4ae44 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/ErrorForWrongTypeOldParameter.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/ErrorForWrongTypeOldParameter.cs @@ -5,7 +5,7 @@ namespace SyncVarHookTests.ErrorForWrongTypeOldParameter class ErrorForWrongTypeOldParameter : NetworkBehaviour { [SyncVar(hook = nameof(onChangeHealth))] - int health; + int health { get; set; } void onChangeHealth(float wrongOldValue, int newValue) { diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/ErrorWhenEventArgsAreWrong.cs b/Assets/Tests/Weaver/SyncVarHookTests~/ErrorWhenEventArgsAreWrong.cs index 1fb722e850c..fbbb5e70e70 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/ErrorWhenEventArgsAreWrong.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/ErrorWhenEventArgsAreWrong.cs @@ -6,7 +6,7 @@ namespace SyncVarHookTests.ErrorWhenEventArgsAreWrong class ErrorWhenEventArgsAreWrong : NetworkBehaviour { [SyncVar(hook = nameof(OnChangeHealth))] - int health; + int health { get; set; } public event Action OnChangeHealth; } diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/ErrorWhenHookNotAction.cs b/Assets/Tests/Weaver/SyncVarHookTests~/ErrorWhenHookNotAction.cs index e870ee05ef7..55e171df530 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/ErrorWhenHookNotAction.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/ErrorWhenHookNotAction.cs @@ -7,7 +7,7 @@ namespace SyncVarHookTests.ErrorWhenHookNotAction class ErrorWhenHookNotAction : NetworkBehaviour { [SyncVar(hook = nameof(OnChangeHealth))] - int health; + int health { get; set; } public event DoStuff OnChangeHealth; } diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/ErrorWhenNoHookFound.cs b/Assets/Tests/Weaver/SyncVarHookTests~/ErrorWhenNoHookFound.cs index a142ea638bb..406b0a65e52 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/ErrorWhenNoHookFound.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/ErrorWhenNoHookFound.cs @@ -5,6 +5,6 @@ namespace SyncVarHookTests.ErrorWhenNoHookFound class ErrorWhenNoHookFound : NetworkBehaviour { [SyncVar(hook = "onChangeHealth")] - int health; + int health { get; set; } } } diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/ErrorWhenNoHookWithCorrectParametersFound.cs b/Assets/Tests/Weaver/SyncVarHookTests~/ErrorWhenNoHookWithCorrectParametersFound.cs index 11265aeb9cf..6496e32d72e 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/ErrorWhenNoHookWithCorrectParametersFound.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/ErrorWhenNoHookWithCorrectParametersFound.cs @@ -5,7 +5,7 @@ namespace SyncVarHookTests.ErrorWhenNoHookWithCorrectParametersFound class ErrorWhenNoHookWithCorrectParametersFound : NetworkBehaviour { [SyncVar(hook = nameof(onChangeHealth))] - int health; + int health { get; set; } void onChangeHealth(int someOtherValue, int moreValue, bool anotherValue) { diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitEvent1Found.cs b/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitEvent1Found.cs index b2cc52cdb3f..24974d3b2e5 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitEvent1Found.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitEvent1Found.cs @@ -6,7 +6,7 @@ namespace SyncVarHookTests.ExplicitEvent1Found class ExplicitEvent1Found : NetworkBehaviour { [SyncVar(hook = nameof(onChangeHealth), hookType = SyncHookType.EventWith1Arg)] - int health; + int health { get; set; } event Action onChangeHealth; } diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitEvent1NotFound.cs b/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitEvent1NotFound.cs index 77676a27f06..072f4bb2e4b 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitEvent1NotFound.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitEvent1NotFound.cs @@ -6,7 +6,7 @@ namespace SyncVarHookTests.ExplicitEvent1NotFound class ExplicitEvent1NotFound : NetworkBehaviour { [SyncVar(hook = nameof(onChangeHealth), hookType = SyncHookType.EventWith1Arg)] - int health; + int health { get; set; } event Action onChangeHealth; } diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitEvent2Found.cs b/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitEvent2Found.cs index d26391e7a8e..f7e8b371676 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitEvent2Found.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitEvent2Found.cs @@ -6,7 +6,7 @@ namespace SyncVarHookTests.ExplicitEvent2Found class ExplicitEvent2Found : NetworkBehaviour { [SyncVar(hook = nameof(onChangeHealth), hookType = SyncHookType.EventWith2Arg)] - int health; + int health { get; set; } event Action onChangeHealth; } diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitEvent2NotFound.cs b/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitEvent2NotFound.cs index 8dad472c90e..81bce5a4420 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitEvent2NotFound.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitEvent2NotFound.cs @@ -5,7 +5,7 @@ namespace SyncVarHookTests.ExplicitEvent2NotFound class ExplicitEvent2NotFound : NetworkBehaviour { [SyncVar(hook = nameof(onChangeHealth), hookType = SyncHookType.EventWith2Arg)] - int health; + int health { get; set; } public void onChangeHealth(int newValue) { diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitMethod1Found.cs b/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitMethod1Found.cs index c3c3ae3fcfb..7ca0698881a 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitMethod1Found.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitMethod1Found.cs @@ -5,7 +5,7 @@ namespace SyncVarHookTests.ExplicitMethod1Found class ExplicitMethod1Found : NetworkBehaviour { [SyncVar(hook = nameof(onChangeHealth), hookType = SyncHookType.MethodWith1Arg)] - int health; + int health { get; set; } public void onChangeHealth(int newValue) { diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitMethod1FoundWithOverLoad.cs b/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitMethod1FoundWithOverLoad.cs index c429fc4afdf..1ceb5a27215 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitMethod1FoundWithOverLoad.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitMethod1FoundWithOverLoad.cs @@ -5,7 +5,7 @@ namespace SyncVarHookTests.ExplicitMethod1FoundWithOverLoad class ExplicitMethod1FoundWithOverLoad : NetworkBehaviour { [SyncVar(hook = nameof(onChangeHealth), hookType = SyncHookType.MethodWith1Arg)] - int health; + int health { get; set; } public void onChangeHealth(int newValue) { diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitMethod1NotFound.cs b/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitMethod1NotFound.cs index f12ad17e676..323318147f5 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitMethod1NotFound.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitMethod1NotFound.cs @@ -6,7 +6,7 @@ namespace SyncVarHookTests.ExplicitMethod1NotFound class ExplicitMethod1NotFound : NetworkBehaviour { [SyncVar(hook = nameof(onChangeHealth), hookType = SyncHookType.MethodWith1Arg)] - int health; + int health { get; set; } event Action onChangeHealth; } diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitMethod2Found.cs b/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitMethod2Found.cs index 87ab8d63d08..09dd990bcc9 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitMethod2Found.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitMethod2Found.cs @@ -5,7 +5,7 @@ namespace SyncVarHookTests.ExplicitMethod2Found class ExplicitMethod2Found : NetworkBehaviour { [SyncVar(hook = nameof(onChangeHealth), hookType = SyncHookType.MethodWith2Arg)] - int health; + int health { get; set; } public void onChangeHealth(int oldValue, int newValue) { diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitMethod2FoundWithOverLoad.cs b/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitMethod2FoundWithOverLoad.cs index 9694f9fe8b2..8ce9d7f4fcc 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitMethod2FoundWithOverLoad.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitMethod2FoundWithOverLoad.cs @@ -5,7 +5,7 @@ namespace SyncVarHookTests.ExplicitMethod2FoundWithOverLoad class ExplicitMethod2FoundWithOverLoad : NetworkBehaviour { [SyncVar(hook = nameof(onChangeHealth), hookType = SyncHookType.MethodWith2Arg)] - int health; + int health { get; set; } public void onChangeHealth(int newValue) { diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitMethod2NotFound.cs b/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitMethod2NotFound.cs index e8a651776b2..6e892d156fa 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitMethod2NotFound.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/ExplicitMethod2NotFound.cs @@ -5,7 +5,7 @@ namespace SyncVarHookTests.ExplicitMethod2NotFound class ExplicitMethod2NotFound : NetworkBehaviour { [SyncVar(hook = nameof(onChangeHealth), hookType = SyncHookType.MethodWith2Arg)] - int health; + int health { get; set; } public void onChangeHealth(int newValue) { diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/FindsHookEvent.cs b/Assets/Tests/Weaver/SyncVarHookTests~/FindsHookEvent.cs index b6945293de5..34f1606b949 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/FindsHookEvent.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/FindsHookEvent.cs @@ -6,7 +6,7 @@ namespace SyncVarHookTests.FindsHookEvent class FindsHookEvent : NetworkBehaviour { [SyncVar(hook = nameof(OnChangeHealth))] - int health; + int health { get; set; } public event Action OnChangeHealth; } diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/FindsHookWithGameObject.cs b/Assets/Tests/Weaver/SyncVarHookTests~/FindsHookWithGameObject.cs index 1279e581e2d..e5bd0aa4a9a 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/FindsHookWithGameObject.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/FindsHookWithGameObject.cs @@ -6,7 +6,7 @@ namespace SyncVarHookTests.FindsHookWithGameObject class FindsHookWithGameObject : NetworkBehaviour { [SyncVar(hook = nameof(onTargetChanged))] - GameObject target; + GameObject target { get; set; } void onTargetChanged(GameObject oldValue, GameObject newValue) { diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/FindsHookWithNetworkIdentity.cs b/Assets/Tests/Weaver/SyncVarHookTests~/FindsHookWithNetworkIdentity.cs index 0b72c264728..05e906229a0 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/FindsHookWithNetworkIdentity.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/FindsHookWithNetworkIdentity.cs @@ -6,7 +6,7 @@ namespace SyncVarHookTests.FindsHookWithNetworkIdentity class FindsHookWithNetworkIdentity : NetworkBehaviour { [SyncVar(hook = nameof(onTargetChanged))] - NetworkIdentity target; + NetworkIdentity target { get; set; } void onTargetChanged(NetworkIdentity oldValue, NetworkIdentity newValue) { diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/FindsHookWithOtherOverloadsInOrder.cs b/Assets/Tests/Weaver/SyncVarHookTests~/FindsHookWithOtherOverloadsInOrder.cs index 60973c522a5..3c41df8f0b0 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/FindsHookWithOtherOverloadsInOrder.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/FindsHookWithOtherOverloadsInOrder.cs @@ -6,7 +6,7 @@ namespace SyncVarHookTests.FindsHookWithOtherOverloadsInOrder class FindsHookWithOtherOverloadsInOrder : NetworkBehaviour { [SyncVar(hook = nameof(onChangeHealth))] - int health; + int health { get; set; } void onChangeHealth(int oldValue, int newValue) { diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/FindsHookWithOtherOverloadsInReverseOrder.cs b/Assets/Tests/Weaver/SyncVarHookTests~/FindsHookWithOtherOverloadsInReverseOrder.cs index 04a62eb1f17..a89caeca708 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/FindsHookWithOtherOverloadsInReverseOrder.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/FindsHookWithOtherOverloadsInReverseOrder.cs @@ -6,7 +6,7 @@ namespace SyncVarHookTests.FindsHookWithOtherOverloadsInReverseOrder class FindsHookWithOtherOverloadsInReverseOrder : NetworkBehaviour { [SyncVar(hook = nameof(onChangeHealth))] - int health; + int health { get; set; } void onChangeHealth(Vector3 anotherValue, bool secondArg) { diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/FindsPrivateHook.cs b/Assets/Tests/Weaver/SyncVarHookTests~/FindsPrivateHook.cs index fcd093ad55d..152ef1e54bc 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/FindsPrivateHook.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/FindsPrivateHook.cs @@ -5,7 +5,7 @@ namespace SyncVarHookTests.FindsPrivateHook class FindsPrivateHook : NetworkBehaviour { [SyncVar(hook = nameof(onChangeHealth))] - int health; + int health { get; set; } void onChangeHealth(int oldValue, int newValue) { diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/FindsPublicHook.cs b/Assets/Tests/Weaver/SyncVarHookTests~/FindsPublicHook.cs index bacdb20b929..d58c98d990a 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/FindsPublicHook.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/FindsPublicHook.cs @@ -5,7 +5,7 @@ namespace SyncVarHookTests.FindsPublicHook class FindsPublicHook : NetworkBehaviour { [SyncVar(hook = nameof(onChangeHealth))] - int health; + int health { get; set; } public void onChangeHealth(int oldValue, int newValue) { diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/FindsStaticHook.cs b/Assets/Tests/Weaver/SyncVarHookTests~/FindsStaticHook.cs index 21aaeb204e2..db5b323f54a 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/FindsStaticHook.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/FindsStaticHook.cs @@ -1,11 +1,11 @@ -using Mirage; +using Mirage; namespace SyncVarHookTests.FindsStaticHook { class FindsStaticHook : NetworkBehaviour { [SyncVar(hook = nameof(onChangeHealth))] - int health; + int health { get; set; } static void onChangeHealth(int oldValue, int newValue) { diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/SuccessGenericAction.cs b/Assets/Tests/Weaver/SyncVarHookTests~/SuccessGenericAction.cs index 0417d90e2ee..2068218ff57 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/SuccessGenericAction.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/SuccessGenericAction.cs @@ -6,7 +6,7 @@ namespace SyncVarHookTests.SuccessGenericAction class SuccessGenericAction : NetworkBehaviour { [SyncVar(hook = nameof(OnChangeHealth))] - int health; + int health { get; set; } public event Action OnChangeHealth; } diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/SyncVarHookServer.cs b/Assets/Tests/Weaver/SyncVarHookTests~/SyncVarHookServer.cs index 06de6e8027e..8566bdedecd 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/SyncVarHookServer.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/SyncVarHookServer.cs @@ -5,7 +5,7 @@ namespace SyncVarHookTests.SyncVarHookServer class SyncVarHookServer : NetworkBehaviour { [SyncVar(hook = nameof(onChangeHealth), invokeHookOnServer = true)] - int health; + int health { get; set; } void onChangeHealth(int oldValue, int newValue) { diff --git a/Assets/Tests/Weaver/SyncVarHookTests~/SyncVarHookServerError.cs b/Assets/Tests/Weaver/SyncVarHookTests~/SyncVarHookServerError.cs index 072672f39f9..6f30acb732a 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests~/SyncVarHookServerError.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests~/SyncVarHookServerError.cs @@ -5,6 +5,6 @@ namespace SyncVarHookTests.SyncVarHookServerError class SyncVarHookServerError : NetworkBehaviour { [SyncVar(invokeHookOnServer = true)] - int health; + int health { get; set; } } } diff --git a/Assets/Tests/Weaver/SyncVarTests~/SyncVarArraySegment.cs b/Assets/Tests/Weaver/SyncVarTests~/SyncVarArraySegment.cs index 9a505fa0ee3..905776af1ad 100644 --- a/Assets/Tests/Weaver/SyncVarTests~/SyncVarArraySegment.cs +++ b/Assets/Tests/Weaver/SyncVarTests~/SyncVarArraySegment.cs @@ -6,6 +6,6 @@ namespace SyncVarTests.SyncVarArraySegment class SyncVarArraySegment : NetworkBehaviour { [SyncVar] - public ArraySegment data; + public ArraySegment data { get; set; } } } diff --git a/Assets/Tests/Weaver/SyncVarTests~/SyncVarsCantBeArray.cs b/Assets/Tests/Weaver/SyncVarTests~/SyncVarsCantBeArray.cs index 1821d049dbb..67f28439f0f 100644 --- a/Assets/Tests/Weaver/SyncVarTests~/SyncVarsCantBeArray.cs +++ b/Assets/Tests/Weaver/SyncVarTests~/SyncVarsCantBeArray.cs @@ -5,6 +5,6 @@ namespace SyncVarTests.SyncVarsCantBeArray class SyncVarsCantBeArray : NetworkBehaviour { [SyncVar] - int[] thisShouldntWork = new int[100]; + int[] thisShouldntWork { get; set; } = new int[100]; } } diff --git a/Assets/Tests/Weaver/SyncVarTests~/SyncVarsDerivedNetworkBehaviour.cs b/Assets/Tests/Weaver/SyncVarTests~/SyncVarsDerivedNetworkBehaviour.cs index b1e0384dc4c..d977bbb554a 100644 --- a/Assets/Tests/Weaver/SyncVarTests~/SyncVarsDerivedNetworkBehaviour.cs +++ b/Assets/Tests/Weaver/SyncVarTests~/SyncVarsDerivedNetworkBehaviour.cs @@ -9,6 +9,6 @@ class MyBehaviour : NetworkBehaviour class SyncVarsDerivedNetworkBehaviour : NetworkBehaviour { [SyncVar] - MyBehaviour invalidVar; + MyBehaviour invalidVar { get; set; } } } diff --git a/Assets/Tests/Weaver/SyncVarTests~/SyncVarsDifferentModule.cs b/Assets/Tests/Weaver/SyncVarTests~/SyncVarsDifferentModule.cs index 0fae4e55998..5fdfdf7fa25 100644 --- a/Assets/Tests/Weaver/SyncVarTests~/SyncVarsDifferentModule.cs +++ b/Assets/Tests/Weaver/SyncVarTests~/SyncVarsDifferentModule.cs @@ -6,10 +6,10 @@ namespace SyncVarTests.SyncVarsDifferentModule class SyncVarsDifferentModule : NetworkBehaviour { [SyncVar(hook = nameof(OnChangeHealth))] - int health; + int health { get; set; } [SyncVar] - TextMesh invalidVar; + TextMesh invalidVar { get; set; } public void TakeDamage(int amount) { diff --git a/Assets/Tests/Weaver/SyncVarTests~/SyncVarsGenericField.cs b/Assets/Tests/Weaver/SyncVarTests~/SyncVarsGenericField.cs index 8a71b6d73e5..f5718db6c83 100644 --- a/Assets/Tests/Weaver/SyncVarTests~/SyncVarsGenericField.cs +++ b/Assets/Tests/Weaver/SyncVarTests~/SyncVarsGenericField.cs @@ -5,6 +5,6 @@ namespace SyncVarTests.SyncVarGenericFields class SyncVarGenericFields : NetworkBehaviour { [SyncVar] - T invalidVar; + T invalidVar { get; set; } } } diff --git a/Assets/Tests/Weaver/SyncVarTests~/SyncVarsGenericParam.cs b/Assets/Tests/Weaver/SyncVarTests~/SyncVarsGenericParam.cs index 7e77bd8b19e..0831e067faa 100644 --- a/Assets/Tests/Weaver/SyncVarTests~/SyncVarsGenericParam.cs +++ b/Assets/Tests/Weaver/SyncVarTests~/SyncVarsGenericParam.cs @@ -10,6 +10,6 @@ struct MySyncVar } [SyncVar] - MySyncVar invalidVar = new MySyncVar(); + MySyncVar invalidVar { get; set; } = new MySyncVar(); } } diff --git a/Assets/Tests/Weaver/SyncVarTests~/SyncVarsInterface.cs b/Assets/Tests/Weaver/SyncVarTests~/SyncVarsInterface.cs index e496ef53ebf..83d266a736f 100644 --- a/Assets/Tests/Weaver/SyncVarTests~/SyncVarsInterface.cs +++ b/Assets/Tests/Weaver/SyncVarTests~/SyncVarsInterface.cs @@ -9,6 +9,6 @@ interface IMySyncVar void interfaceMethod(); } [SyncVar] - IMySyncVar invalidVar; + IMySyncVar invalidVar { get; set; } } } diff --git a/Assets/Tests/Weaver/SyncVarTests~/SyncVarsMoreThan63.cs b/Assets/Tests/Weaver/SyncVarTests~/SyncVarsMoreThan63.cs index dcf1459d40c..c3d76b59633 100644 --- a/Assets/Tests/Weaver/SyncVarTests~/SyncVarsMoreThan63.cs +++ b/Assets/Tests/Weaver/SyncVarTests~/SyncVarsMoreThan63.cs @@ -5,71 +5,71 @@ namespace SyncVarTests.SyncVarsMoreThan63 class SyncVarsMoreThan63 : NetworkBehaviour { [SyncVar(hook = nameof(OnChangeHealth))] - int health; + int health { get; set; } - [SyncVar] int var2; - [SyncVar] int var3; - [SyncVar] int var4; - [SyncVar] int var5; - [SyncVar] int var6; - [SyncVar] int var7; - [SyncVar] int var8; - [SyncVar] int var9; - [SyncVar] int var10; - [SyncVar] int var11; - [SyncVar] int var12; - [SyncVar] int var13; - [SyncVar] int var14; - [SyncVar] int var15; - [SyncVar] int var16; - [SyncVar] int var17; - [SyncVar] int var18; - [SyncVar] int var19; - [SyncVar] int var20; - [SyncVar] int var21; - [SyncVar] int var22; - [SyncVar] int var23; - [SyncVar] int var24; - [SyncVar] int var25; - [SyncVar] int var26; - [SyncVar] int var27; - [SyncVar] int var28; - [SyncVar] int var29; - [SyncVar] int var30; - [SyncVar] int var31; - [SyncVar] int var32; - [SyncVar] int var33; - [SyncVar] int var34; - [SyncVar] int var35; - [SyncVar] int var36; - [SyncVar] int var37; - [SyncVar] int var38; - [SyncVar] int var39; - [SyncVar] int var40; - [SyncVar] int var41; - [SyncVar] int var42; - [SyncVar] int var43; - [SyncVar] int var44; - [SyncVar] int var45; - [SyncVar] int var46; - [SyncVar] int var47; - [SyncVar] int var48; - [SyncVar] int var49; - [SyncVar] int var50; - [SyncVar] int var51; - [SyncVar] int var52; - [SyncVar] int var53; - [SyncVar] int var54; - [SyncVar] int var55; - [SyncVar] int var56; - [SyncVar] int var57; - [SyncVar] int var58; - [SyncVar] int var59; - [SyncVar] int var60; - [SyncVar] int var61; - [SyncVar] int var62; - [SyncVar] int var63; - [SyncVar] int var64; + [SyncVar] int var2 { get; set; } + [SyncVar] int var3 { get; set; } + [SyncVar] int var4 { get; set; } + [SyncVar] int var5 { get; set; } + [SyncVar] int var6 { get; set; } + [SyncVar] int var7 { get; set; } + [SyncVar] int var8 { get; set; } + [SyncVar] int var9 { get; set; } + [SyncVar] int var10 { get; set; } + [SyncVar] int var11 { get; set; } + [SyncVar] int var12 { get; set; } + [SyncVar] int var13 { get; set; } + [SyncVar] int var14 { get; set; } + [SyncVar] int var15 { get; set; } + [SyncVar] int var16 { get; set; } + [SyncVar] int var17 { get; set; } + [SyncVar] int var18 { get; set; } + [SyncVar] int var19 { get; set; } + [SyncVar] int var20 { get; set; } + [SyncVar] int var21 { get; set; } + [SyncVar] int var22 { get; set; } + [SyncVar] int var23 { get; set; } + [SyncVar] int var24 { get; set; } + [SyncVar] int var25 { get; set; } + [SyncVar] int var26 { get; set; } + [SyncVar] int var27 { get; set; } + [SyncVar] int var28 { get; set; } + [SyncVar] int var29 { get; set; } + [SyncVar] int var30 { get; set; } + [SyncVar] int var31 { get; set; } + [SyncVar] int var32 { get; set; } + [SyncVar] int var33 { get; set; } + [SyncVar] int var34 { get; set; } + [SyncVar] int var35 { get; set; } + [SyncVar] int var36 { get; set; } + [SyncVar] int var37 { get; set; } + [SyncVar] int var38 { get; set; } + [SyncVar] int var39 { get; set; } + [SyncVar] int var40 { get; set; } + [SyncVar] int var41 { get; set; } + [SyncVar] int var42 { get; set; } + [SyncVar] int var43 { get; set; } + [SyncVar] int var44 { get; set; } + [SyncVar] int var45 { get; set; } + [SyncVar] int var46 { get; set; } + [SyncVar] int var47 { get; set; } + [SyncVar] int var48 { get; set; } + [SyncVar] int var49 { get; set; } + [SyncVar] int var50 { get; set; } + [SyncVar] int var51 { get; set; } + [SyncVar] int var52 { get; set; } + [SyncVar] int var53 { get; set; } + [SyncVar] int var54 { get; set; } + [SyncVar] int var55 { get; set; } + [SyncVar] int var56 { get; set; } + [SyncVar] int var57 { get; set; } + [SyncVar] int var58 { get; set; } + [SyncVar] int var59 { get; set; } + [SyncVar] int var60 { get; set; } + [SyncVar] int var61 { get; set; } + [SyncVar] int var62 { get; set; } + [SyncVar] int var63 { get; set; } + [SyncVar] int var64 { get; set; } public void TakeDamage(int amount) { diff --git a/Assets/Tests/Weaver/SyncVarTests~/SyncVarsStatic.cs b/Assets/Tests/Weaver/SyncVarTests~/SyncVarsStatic.cs index b829c838ef9..a5f0727b1d9 100644 --- a/Assets/Tests/Weaver/SyncVarTests~/SyncVarsStatic.cs +++ b/Assets/Tests/Weaver/SyncVarTests~/SyncVarsStatic.cs @@ -5,6 +5,6 @@ namespace SyncVarTests.SyncVarsStatic class SyncVarsStatic : NetworkBehaviour { [SyncVar] - static int invalidVar = 123; + static int invalidVar { get; set; } = 123; } } diff --git a/Assets/Tests/Weaver/SyncVarTests~/SyncVarsSyncList.cs b/Assets/Tests/Weaver/SyncVarTests~/SyncVarsSyncList.cs index 26d734ce24a..0d223d8b67a 100644 --- a/Assets/Tests/Weaver/SyncVarTests~/SyncVarsSyncList.cs +++ b/Assets/Tests/Weaver/SyncVarTests~/SyncVarsSyncList.cs @@ -7,6 +7,6 @@ namespace SyncVarTests.SyncVarsSyncList class SyncVarsSyncList : NetworkBehaviour { [SyncVar] - SyncList syncints; + SyncList syncints { get; set; } } } diff --git a/Assets/Tests/Weaver/SyncVarTests~/SyncVarsUnityComponent.cs b/Assets/Tests/Weaver/SyncVarTests~/SyncVarsUnityComponent.cs index e1145b6a1a9..5a6481ad048 100644 --- a/Assets/Tests/Weaver/SyncVarTests~/SyncVarsUnityComponent.cs +++ b/Assets/Tests/Weaver/SyncVarTests~/SyncVarsUnityComponent.cs @@ -6,6 +6,6 @@ namespace SyncVarTests.SyncVarsUnityComponent class SyncVarsUnityComponent : NetworkBehaviour { [SyncVar] - TextMesh invalidVar; + TextMesh invalidVar { get; set; } } } diff --git a/Assets/Tests/Weaver/SyncVarTests~/SyncVarsValid.cs b/Assets/Tests/Weaver/SyncVarTests~/SyncVarsValid.cs index 46c6c3f72a4..ad0e00e45d6 100644 --- a/Assets/Tests/Weaver/SyncVarTests~/SyncVarsValid.cs +++ b/Assets/Tests/Weaver/SyncVarTests~/SyncVarsValid.cs @@ -5,70 +5,70 @@ namespace SyncVarTests.SyncVarsValid class SyncVarsValid : NetworkBehaviour { [SyncVar(hook = nameof(OnChangeHealth))] - int health; + int health { get; set; } - [SyncVar] int var2; - [SyncVar] int var3; - [SyncVar] int var4; - [SyncVar] int var5; - [SyncVar] int var6; - [SyncVar] int var7; - [SyncVar] int var8; - [SyncVar] int var9; - [SyncVar] int var10; - [SyncVar] int var11; - [SyncVar] int var12; - [SyncVar] int var13; - [SyncVar] int var14; - [SyncVar] int var15; - [SyncVar] int var16; - [SyncVar] int var17; - [SyncVar] int var18; - [SyncVar] int var19; - [SyncVar] int var20; - [SyncVar] int var21; - [SyncVar] int var22; - [SyncVar] int var23; - [SyncVar] int var24; - [SyncVar] int var25; - [SyncVar] int var26; - [SyncVar] int var27; - [SyncVar] int var28; - [SyncVar] int var29; - [SyncVar] int var30; - [SyncVar] int var31; - [SyncVar] int var32; - [SyncVar] int var33; - [SyncVar] int var34; - [SyncVar] int var35; - [SyncVar] int var36; - [SyncVar] int var37; - [SyncVar] int var38; - [SyncVar] int var39; - [SyncVar] int var40; - [SyncVar] int var41; - [SyncVar] int var42; - [SyncVar] int var43; - [SyncVar] int var44; - [SyncVar] int var45; - [SyncVar] int var46; - [SyncVar] int var47; - [SyncVar] int var48; - [SyncVar] int var49; - [SyncVar] int var50; - [SyncVar] int var51; - [SyncVar] int var52; - [SyncVar] int var53; - [SyncVar] int var54; - [SyncVar] int var55; - [SyncVar] int var56; - [SyncVar] int var57; - [SyncVar] int var58; - [SyncVar] int var59; - [SyncVar] int var60; - [SyncVar] int var61; - [SyncVar] int var62; - [SyncVar] int var63; + [SyncVar] int var2 { get; set; } + [SyncVar] int var3 { get; set; } + [SyncVar] int var4 { get; set; } + [SyncVar] int var5 { get; set; } + [SyncVar] int var6 { get; set; } + [SyncVar] int var7 { get; set; } + [SyncVar] int var8 { get; set; } + [SyncVar] int var9 { get; set; } + [SyncVar] int var10 { get; set; } + [SyncVar] int var11 { get; set; } + [SyncVar] int var12 { get; set; } + [SyncVar] int var13 { get; set; } + [SyncVar] int var14 { get; set; } + [SyncVar] int var15 { get; set; } + [SyncVar] int var16 { get; set; } + [SyncVar] int var17 { get; set; } + [SyncVar] int var18 { get; set; } + [SyncVar] int var19 { get; set; } + [SyncVar] int var20 { get; set; } + [SyncVar] int var21 { get; set; } + [SyncVar] int var22 { get; set; } + [SyncVar] int var23 { get; set; } + [SyncVar] int var24 { get; set; } + [SyncVar] int var25 { get; set; } + [SyncVar] int var26 { get; set; } + [SyncVar] int var27 { get; set; } + [SyncVar] int var28 { get; set; } + [SyncVar] int var29 { get; set; } + [SyncVar] int var30 { get; set; } + [SyncVar] int var31 { get; set; } + [SyncVar] int var32 { get; set; } + [SyncVar] int var33 { get; set; } + [SyncVar] int var34 { get; set; } + [SyncVar] int var35 { get; set; } + [SyncVar] int var36 { get; set; } + [SyncVar] int var37 { get; set; } + [SyncVar] int var38 { get; set; } + [SyncVar] int var39 { get; set; } + [SyncVar] int var40 { get; set; } + [SyncVar] int var41 { get; set; } + [SyncVar] int var42 { get; set; } + [SyncVar] int var43 { get; set; } + [SyncVar] int var44 { get; set; } + [SyncVar] int var45 { get; set; } + [SyncVar] int var46 { get; set; } + [SyncVar] int var47 { get; set; } + [SyncVar] int var48 { get; set; } + [SyncVar] int var49 { get; set; } + [SyncVar] int var50 { get; set; } + [SyncVar] int var51 { get; set; } + [SyncVar] int var52 { get; set; } + [SyncVar] int var53 { get; set; } + [SyncVar] int var54 { get; set; } + [SyncVar] int var55 { get; set; } + [SyncVar] int var56 { get; set; } + [SyncVar] int var57 { get; set; } + [SyncVar] int var58 { get; set; } + [SyncVar] int var59 { get; set; } + [SyncVar] int var60 { get; set; } + [SyncVar] int var61 { get; set; } + [SyncVar] int var62 { get; set; } + [SyncVar] int var63 { get; set; } public void TakeDamage(int amount) { diff --git a/Assets/Tests/Weaver/SyncVarTests~/SyncVarsValidInitialOnly.cs b/Assets/Tests/Weaver/SyncVarTests~/SyncVarsValidInitialOnly.cs index 369e729c51b..e72f3def317 100644 --- a/Assets/Tests/Weaver/SyncVarTests~/SyncVarsValidInitialOnly.cs +++ b/Assets/Tests/Weaver/SyncVarTests~/SyncVarsValidInitialOnly.cs @@ -4,7 +4,7 @@ namespace SyncVarTests.SyncVarsValidInitialOnly { class SyncVarsValidInitialOnly : NetworkBehaviour { - [SyncVar(initialOnly = true)] int var; - [SyncVar(initialOnly = false)] int var2; + [SyncVar(initialOnly = true)] int var { get; set; } + [SyncVar(initialOnly = false)] int var2 { get; set; } } } From 9962bfc511509c6e25c86fab6e7fc45f11c18bfe Mon Sep 17 00:00:00 2001 From: James Frowen Date: Sat, 30 May 2026 23:35:36 +0100 Subject: [PATCH 05/41] docs: update guides and code examples to property-based SyncVars --- doc/docs/guides/bit-packing/bit-count-from-range.md | 6 +++--- doc/docs/guides/bit-packing/bit-count.md | 6 +++--- doc/docs/guides/bit-packing/float-pack.md | 6 +++--- doc/docs/guides/bit-packing/quaternion-pack.md | 4 ++-- doc/docs/guides/bit-packing/var-int-blocks.md | 6 +++--- doc/docs/guides/bit-packing/vector-pack.md | 10 +++++----- doc/docs/guides/bit-packing/zig-zag-encode.md | 4 ++-- .../community-guides/mirage-quick-start-guide.md | 8 ++++---- doc/docs/guides/game-objects/pickup-drop-child.md | 4 ++-- doc/docs/guides/game-objects/player-proxy-pattern.md | 6 +++--- doc/docs/guides/game-objects/spawn-object.md | 2 +- doc/docs/guides/remote-actions/rpc-examples.md | 4 ++-- doc/docs/guides/remote-actions/server-rpc.md | 2 +- doc/docs/guides/serialization/data-types.md | 2 +- doc/docs/guides/serialization/generics.md | 2 +- doc/docs/guides/sync/code-generation.md | 6 +++--- doc/docs/guides/sync/sync-var.md | 12 ++++++------ 17 files changed, 45 insertions(+), 45 deletions(-) diff --git a/doc/docs/guides/bit-packing/bit-count-from-range.md b/doc/docs/guides/bit-packing/bit-count-from-range.md index 60b1ee2486a..ab5317b70cb 100644 --- a/doc/docs/guides/bit-packing/bit-count-from-range.md +++ b/doc/docs/guides/bit-packing/bit-count-from-range.md @@ -33,7 +33,7 @@ A modifier that can add to a character value to increase or decrease it public class MyNetworkBehaviour : NetworkBehaviour { [SyncVar, BitCountFromRange(-100, 100)] - public int modifier; + public int modifier { get; set; } } ``` @@ -64,7 +64,7 @@ public enum MyDirection public class MyNetworkBehaviour : NetworkBehaviour { [SyncVar, BitCount(-1, 1)] - public MyDirection direction; + public MyDirection direction { get; set; } } ``` @@ -80,7 +80,7 @@ public class MyNetworkBehaviour : NetworkBehaviour Source: ```cs [SyncVar, BitCountFromRange(-100, 100)] -public int myValue; +public int myValue { get; set; } ``` Generated: diff --git a/doc/docs/guides/bit-packing/bit-count.md b/doc/docs/guides/bit-packing/bit-count.md index 407edb62983..c8c2eb9831d 100644 --- a/doc/docs/guides/bit-packing/bit-count.md +++ b/doc/docs/guides/bit-packing/bit-count.md @@ -32,7 +32,7 @@ Health which is between 0 and 100 public class MyNetworkBehaviour : NetworkBehaviour { [SyncVar, BitCount(7)] - public int Health; + public int Health { get; set; } } ``` @@ -52,7 +52,7 @@ Weapon index in a list of 6 weapons public class MyNetworkBehaviour : NetworkBehaviour { [SyncVar, BitCount(3)] - public int WeaponIndex; + public int WeaponIndex { get; set; } } ``` @@ -66,7 +66,7 @@ public class MyNetworkBehaviour : NetworkBehaviour Source: ```cs [SyncVar, BitCount(7)] -public int myValue; +public int myValue { get; set; } ``` Generated: diff --git a/doc/docs/guides/bit-packing/float-pack.md b/doc/docs/guides/bit-packing/float-pack.md index f088a3eac2c..e0bb82fe220 100644 --- a/doc/docs/guides/bit-packing/float-pack.md +++ b/doc/docs/guides/bit-packing/float-pack.md @@ -26,7 +26,7 @@ Health which is between 0 and 100 public class MyNetworkBehaviour : NetworkBehaviour { [SyncVar, FloatPack(100f, 0.02f)] - public int Health; + public int Health { get; set; } } ``` @@ -47,7 +47,7 @@ A Percent that where you only want to send 8 bits public class MyNetworkBehaviour : NetworkBehaviour { [SyncVar, FloatPack(1f, 8)] - public int Percent; + public int Percent { get; set; } } ``` @@ -58,7 +58,7 @@ public class MyNetworkBehaviour : NetworkBehaviour Source: ```cs [SyncVar, FloatPack(100f, 0.02f)] -public int myValue; +public int myValue { get; set; } ``` Generated: diff --git a/doc/docs/guides/bit-packing/quaternion-pack.md b/doc/docs/guides/bit-packing/quaternion-pack.md index 7ca9745bf63..5191fade0f6 100644 --- a/doc/docs/guides/bit-packing/quaternion-pack.md +++ b/doc/docs/guides/bit-packing/quaternion-pack.md @@ -56,7 +56,7 @@ The precision of the smallest 3 can in increased or decreased to change the bit public class MyNetworkBehaviour : NetworkBehaviour { [SyncVar, QuaternionPack(9)] - public Quaternion direction; + public Quaternion direction { get; set; } } ``` @@ -65,7 +65,7 @@ public class MyNetworkBehaviour : NetworkBehaviour Source: ```cs [SyncVar, QuaternionPack(9)] -public int myValue; +public int myValue { get; set; } ``` Generated: diff --git a/doc/docs/guides/bit-packing/var-int-blocks.md b/doc/docs/guides/bit-packing/var-int-blocks.md index 7224ac6d3e2..7466ebcad05 100644 --- a/doc/docs/guides/bit-packing/var-int-blocks.md +++ b/doc/docs/guides/bit-packing/var-int-blocks.md @@ -25,7 +25,7 @@ A modifier that can be added to a character value to increase or decrease it public class MyNetworkBehaviour : NetworkBehaviour { [SyncVar, VarIntBlocks(-100, 100)] - public int modifier; + public int modifier { get; set; } } ``` @@ -56,7 +56,7 @@ public enum MyDirection public class MyNetworkBehaviour : NetworkBehaviour { [SyncVar, BitCount(-1, 1)] - public MyDirection direction; + public MyDirection direction { get; set; } } ``` @@ -72,7 +72,7 @@ public class MyNetworkBehaviour : NetworkBehaviour Source: ```cs [SyncVar, BitCountFromRange(-100, 100)] -public int myValue; +public int myValue { get; set; } ``` Generated: diff --git a/doc/docs/guides/bit-packing/vector-pack.md b/doc/docs/guides/bit-packing/vector-pack.md index 93d19359783..f8dde0eeee7 100644 --- a/doc/docs/guides/bit-packing/vector-pack.md +++ b/doc/docs/guides/bit-packing/vector-pack.md @@ -21,7 +21,7 @@ A Position in bounds +-100 in all XYZ with 0.05 precision for all axis public class MyNetworkBehaviour : NetworkBehaviour { [SyncVar, Vector3Pack(100f, 100f, 100f, 0.05f)] - public Vector3 Position; + public Vector3 Position { get; set; } } ``` @@ -33,7 +33,7 @@ A Position in bounds +-100 in all XZ with 0.05 precision, but with +-20 and prec public class MyNetworkBehaviour : NetworkBehaviour { [SyncVar, Vector3Pack(100f, 20f, 100f, 0.05f, 0.1f, 0.05f)] - public Vector3 Position; + public Vector3 Position { get; set; } } ``` @@ -45,7 +45,7 @@ A position in a 2D map public class MyNetworkBehaviour : NetworkBehaviour { [SyncVar, Vector2Pack(1000f, 80f, 0.05f)] - public Vector2 Position; + public Vector2 Position { get; set; } } ``` @@ -54,10 +54,10 @@ public class MyNetworkBehaviour : NetworkBehaviour Source: ```cs [SyncVar, Vector3Pack(100f, 20f, 100f, 0.05f, 0.1f, 0.05f)] -public int myValue1; +public int myValue1 { get; set; } [SyncVar, Vector2Pack(1000f, 80f, 0.05f)] -public int myValue2; +public int myValue2 { get; set; } ``` Generated: diff --git a/doc/docs/guides/bit-packing/zig-zag-encode.md b/doc/docs/guides/bit-packing/zig-zag-encode.md index c90e7cf43bb..e6998d57a73 100644 --- a/doc/docs/guides/bit-packing/zig-zag-encode.md +++ b/doc/docs/guides/bit-packing/zig-zag-encode.md @@ -32,7 +32,7 @@ A modifier that can be added to a character value to increase or decrease it public class MyNetworkBehaviour : NetworkBehaviour { [SyncVar, BitCount(8), ZigZagEncode] - public int modifier; + public int modifier { get; set; } } ``` @@ -51,7 +51,7 @@ public class MyNetworkBehaviour : NetworkBehaviour Source: ```cs [SyncVar, BitCount(8), ZigZagEncode] -public int myValue; +public int myValue { get; set; } ``` Generated: diff --git a/doc/docs/guides/community-guides/mirage-quick-start-guide.md b/doc/docs/guides/community-guides/mirage-quick-start-guide.md index 618575cb29d..15af4a69382 100644 --- a/doc/docs/guides/community-guides/mirage-quick-start-guide.md +++ b/doc/docs/guides/community-guides/mirage-quick-start-guide.md @@ -211,10 +211,10 @@ namespace QuickStart private Material playerMaterialClone; [SyncVar(hook = nameof(OnNameChanged))] - public string playerName; + public string playerName { get; set; } [SyncVar(hook = nameof(OnColorChanged))] - public Color playerColor = Color.white; + public Color playerColor { get; set; } = Color.white; [ServerRpc] public void CmdSetupPlayer(string _name, Color _col) @@ -343,7 +343,7 @@ namespace QuickStart public PlayerScript playerScript; [SyncVar(hook = nameof(OnStatusTextChanged))] - public string statusText; + public string statusText { get; set; } void OnStatusTextChanged(string _Old, string _New) { @@ -392,7 +392,7 @@ private int selectedWeaponLocal = 1; public GameObject[] weaponArray; [SyncVar(hook = nameof(OnWeaponChanged))] -public int activeWeaponSynced; +public int activeWeaponSynced { get; set; } void OnWeaponChanged(int _Old, int _New) { diff --git a/doc/docs/guides/game-objects/pickup-drop-child.md b/doc/docs/guides/game-objects/pickup-drop-child.md index 2a8daf06f23..82453bd9984 100644 --- a/doc/docs/guides/game-objects/pickup-drop-child.md +++ b/doc/docs/guides/game-objects/pickup-drop-child.md @@ -47,7 +47,7 @@ public class PlayerEquip : NetworkBehaviour public GameObject cylinderPrefab; [SyncVar(hook = nameof(OnChangeEquipment))] - public EquippedItem equippedItem; + public EquippedItem equippedItem { get; set; } void OnChangeEquipment(EquippedItem oldEquippedItem, EquippedItem newEquippedItem) { @@ -163,7 +163,7 @@ using Mirage; public class SceneObject : NetworkBehaviour { [SyncVar(hook = nameof(OnChangeEquipment))] - public EquippedItem equippedItem; + public EquippedItem equippedItem { get; set; } public GameObject ballPrefab; public GameObject boxPrefab; diff --git a/doc/docs/guides/game-objects/player-proxy-pattern.md b/doc/docs/guides/game-objects/player-proxy-pattern.md index 5bf9e2349fb..b1608266354 100644 --- a/doc/docs/guides/game-objects/player-proxy-pattern.md +++ b/doc/docs/guides/game-objects/player-proxy-pattern.md @@ -25,11 +25,11 @@ Create a prefab with just a `NetworkIdentity` and your `PlayerContext` script. T ```cs public class PlayerContext : NetworkBehaviour { - [SyncVar] public string PlayerName; - [SyncVar] public string Team; + [SyncVar] public string PlayerName { get; set; } + [SyncVar] public string Team { get; set; } // easy access to the gameplay character via NetworkPlayer.Identity - [SyncVar] public PlayerCharacter ActiveCharacter; + [SyncVar] public PlayerCharacter ActiveCharacter { get; set; } } ``` diff --git a/doc/docs/guides/game-objects/spawn-object.md b/doc/docs/guides/game-objects/spawn-object.md index a0e2861941a..c64ded2904b 100644 --- a/doc/docs/guides/game-objects/spawn-object.md +++ b/doc/docs/guides/game-objects/spawn-object.md @@ -122,7 +122,7 @@ using Mirage; public class Tree : NetworkBehaviour { [SyncVar] - public int numLeaves; + public int numLeaves { get; set; } void Start() { diff --git a/doc/docs/guides/remote-actions/rpc-examples.md b/doc/docs/guides/remote-actions/rpc-examples.md index 4401461ca8d..548a4c7b5bf 100644 --- a/doc/docs/guides/remote-actions/rpc-examples.md +++ b/doc/docs/guides/remote-actions/rpc-examples.md @@ -13,7 +13,7 @@ Set a player's name from a client and have it synced to other players. public class Player : NetworkBehaviour { [SyncVar] - public string PlayerName; + public string PlayerName { get; set; } [ServerRpc] public void RpcChangeName(string newName) @@ -33,7 +33,7 @@ RPCs are registered using the classes static constructor with methods that will public class Player : NetworkBehaviour { [SyncVar] - public string PlayerName; + public string PlayerName { get; set; } [ServerRpc] public void RpcChangeName(string newName) diff --git a/doc/docs/guides/remote-actions/server-rpc.md b/doc/docs/guides/remote-actions/server-rpc.md index 01b98a9072e..40ff3596734 100644 --- a/doc/docs/guides/remote-actions/server-rpc.md +++ b/doc/docs/guides/remote-actions/server-rpc.md @@ -74,7 +74,7 @@ public enum DoorState : byte public class Door : NetworkBehaviour { [SyncVar] - public DoorState doorState; + public DoorState doorState { get; set; } [ServerRpc(requireAuthority = false)] public void CmdSetDoorState(DoorState newDoorState, INetworkPlayer sender = null) diff --git a/doc/docs/guides/serialization/data-types.md b/doc/docs/guides/serialization/data-types.md index aa37cd553c1..ea987ea2265 100644 --- a/doc/docs/guides/serialization/data-types.md +++ b/doc/docs/guides/serialization/data-types.md @@ -41,7 +41,7 @@ You may find that it's more robust to sync the `NetworkIdentity.NetID` (`uint`) public GameObject target; [SyncVar(hook = nameof(OnTargetChanged))] - public uint targetID; + public uint targetID { get; set; } void OnTargetChanged(uint _, uint newValue) { diff --git a/doc/docs/guides/serialization/generics.md b/doc/docs/guides/serialization/generics.md index e0c0a503c8d..0f55057a187 100644 --- a/doc/docs/guides/serialization/generics.md +++ b/doc/docs/guides/serialization/generics.md @@ -13,7 +13,7 @@ By making a [NetworkBehaviour](/docs/guides/game-objects/network-behaviour) gene public class MyGenericBehaviour : NetworkBehaviour { [SyncVar] - public T Value; + public T Value { get; set; } public void MyRpc(T value) { diff --git a/doc/docs/guides/sync/code-generation.md b/doc/docs/guides/sync/code-generation.md index ec9d9de7060..dc51d3968b0 100644 --- a/doc/docs/guides/sync/code-generation.md +++ b/doc/docs/guides/sync/code-generation.md @@ -11,13 +11,13 @@ using Mirage; public class Data : NetworkBehaviour { [SyncVar(hook = nameof(OnInt1Changed))] - public int int1 = 66; + public int int1 { get; set; } = 66; [SyncVar] - public int int2 = 23487; + public int int2 { get; set; } = 23487; [SyncVar] - public string MyString = "Example string"; + public string MyString { get; set; } = "Example string"; void OnInt1Changed(int oldValue, int newValue) { diff --git a/doc/docs/guides/sync/sync-var.md b/doc/docs/guides/sync/sync-var.md index e006485a648..0ed093ea883 100644 --- a/doc/docs/guides/sync/sync-var.md +++ b/doc/docs/guides/sync/sync-var.md @@ -27,7 +27,7 @@ using UnityEngine; public class Player : NetworkBehaviour { [SyncVar] - public int clickCount; + public int clickCount { get; set; } private void Update() { @@ -55,13 +55,13 @@ SyncVars work with class inheritance. Consider this example: private class Pet : NetworkBehaviour { [SyncVar] - private string name; + private string name { get; set; } } private class Cat : Pet { [SyncVar] - private Color32 color; + private Color32 color { get; set; } } ``` @@ -86,7 +86,7 @@ using Mirage; public class Player : NetworkBehaviour { [SyncVar(hook = nameof(UpdateColor))] - private Color playerColor = Color.black; + private Color playerColor { get; set; } = Color.black; private Renderer renderer; @@ -133,7 +133,7 @@ using Mirage; public class Player : NetworkBehaviour { [SyncVar(hook = nameof(UpdateColor), invokeHookOnServer = true)] - private Color playerColor = Color.black; + private Color playerColor { get; set; } = Color.black; private Renderer renderer; @@ -189,7 +189,7 @@ using UnityEngine; public class Player : NetworkBehaviour { [SyncVar(initialOnly = true)] - private int weaponId; + private int weaponId { get; set; } private void Awake() { From 642c74a6040e439534ad2417624b33739a5c407b Mon Sep 17 00:00:00 2001 From: James Frowen Date: Sat, 30 May 2026 23:48:55 +0100 Subject: [PATCH 06/41] refactor: update Samples to use property-based SyncVars --- .../Samples~/AdditiveScenes/Scripts/RandomColor.cs | 2 +- .../AdditiveScenes/Scripts/ShootingTankBehaviour.cs | 2 +- Assets/Mirage/Samples~/Basic/Scripts/BasicPlayer.cs | 6 +++--- Assets/Mirage/Samples~/Chat/Scripts/Player.cs | 2 +- .../Samples~/InterestManagement/Scripts/Tank.cs | 3 +-- .../Mirage/Samples~/MatchScenes/MatchScenesPlayer.cs | 2 +- .../MultipleAdditiveScenes/Scripts/PlayerScore.cs | 8 ++++---- .../MultipleAdditiveScenes/Scripts/RandomColor.cs | 2 +- .../SpawnCustomPlayer/CustomCharacterSpawner.cs | 6 +++--- Assets/Mirage/Samples~/Tanks/Scripts/Tank.cs | 11 +++++------ 10 files changed, 21 insertions(+), 23 deletions(-) diff --git a/Assets/Mirage/Samples~/AdditiveScenes/Scripts/RandomColor.cs b/Assets/Mirage/Samples~/AdditiveScenes/Scripts/RandomColor.cs index fa8a6219e00..ee1a1ceebd0 100644 --- a/Assets/Mirage/Samples~/AdditiveScenes/Scripts/RandomColor.cs +++ b/Assets/Mirage/Samples~/AdditiveScenes/Scripts/RandomColor.cs @@ -16,7 +16,7 @@ public void OnStartServer() // Color32 packs to 4 bytes [SyncVar(hook = nameof(SetColor))] - public Color32 color = Color.black; + public Color32 color { get; set; } = Color.black; // Unity clones the material when GetComponent().material is called // Cache it here and destroy it in OnDestroy to prevent a memory leak diff --git a/Assets/Mirage/Samples~/AdditiveScenes/Scripts/ShootingTankBehaviour.cs b/Assets/Mirage/Samples~/AdditiveScenes/Scripts/ShootingTankBehaviour.cs index aaa768917c2..f5493107c34 100644 --- a/Assets/Mirage/Samples~/AdditiveScenes/Scripts/ShootingTankBehaviour.cs +++ b/Assets/Mirage/Samples~/AdditiveScenes/Scripts/ShootingTankBehaviour.cs @@ -7,7 +7,7 @@ namespace Mirage.Examples.Additive public class ShootingTankBehaviour : NetworkBehaviour { [SyncVar] - public Quaternion rotation; + public Quaternion rotation { get; set; } public Animator animator; diff --git a/Assets/Mirage/Samples~/Basic/Scripts/BasicPlayer.cs b/Assets/Mirage/Samples~/Basic/Scripts/BasicPlayer.cs index 980dc661c5c..51680e06a1a 100644 --- a/Assets/Mirage/Samples~/Basic/Scripts/BasicPlayer.cs +++ b/Assets/Mirage/Samples~/Basic/Scripts/BasicPlayer.cs @@ -19,14 +19,14 @@ public class BasicPlayer : NetworkBehaviour // These are set in OnStartServer and used in OnStartClient [SyncVar(initialOnly = true)] // playerNo is set on spawn so we can use initialOnly so it is only synced once - public int playerNo; + public int playerNo { get; set; } [SyncVar] - private Color playerColor; + private Color playerColor { get; set; } // This is updated by UpdateData which is called from OnStartServer via InvokeRepeating [SyncVar(hook = nameof(OnPlayerDataChanged))] - public int playerData; + public int playerData { get; set; } private void Awake() { diff --git a/Assets/Mirage/Samples~/Chat/Scripts/Player.cs b/Assets/Mirage/Samples~/Chat/Scripts/Player.cs index c384f6da5cd..328a9a0b393 100644 --- a/Assets/Mirage/Samples~/Chat/Scripts/Player.cs +++ b/Assets/Mirage/Samples~/Chat/Scripts/Player.cs @@ -5,7 +5,7 @@ namespace Mirage.Examples.Chat public class Player : NetworkBehaviour { [SyncVar] - public string playerName; + public string playerName { get; set; } public static event Action OnMessage; diff --git a/Assets/Mirage/Samples~/InterestManagement/Scripts/Tank.cs b/Assets/Mirage/Samples~/InterestManagement/Scripts/Tank.cs index 2ee1e25fa27..c282e6e8c56 100644 --- a/Assets/Mirage/Samples~/InterestManagement/Scripts/Tank.cs +++ b/Assets/Mirage/Samples~/InterestManagement/Scripts/Tank.cs @@ -12,9 +12,8 @@ public class Tank : NetworkBehaviour [Header("Movement")] public float rotationSpeed = 100; - [Header("Game Stats")] [SyncVar] - public string playerName; + public string playerName { get; set; } public TextMesh nameText; diff --git a/Assets/Mirage/Samples~/MatchScenes/MatchScenesPlayer.cs b/Assets/Mirage/Samples~/MatchScenes/MatchScenesPlayer.cs index 71e980ba629..e94de88c131 100644 --- a/Assets/Mirage/Samples~/MatchScenes/MatchScenesPlayer.cs +++ b/Assets/Mirage/Samples~/MatchScenes/MatchScenesPlayer.cs @@ -5,7 +5,7 @@ namespace Mirage.Examples.MatchScenes public class MatchScenesPlayer : NetworkBehaviour { [SyncVar] - public Color color; + public Color color { get; set; } private void Awake() { diff --git a/Assets/Mirage/Samples~/MultipleAdditiveScenes/Scripts/PlayerScore.cs b/Assets/Mirage/Samples~/MultipleAdditiveScenes/Scripts/PlayerScore.cs index 2e90676cab8..8f710934669 100644 --- a/Assets/Mirage/Samples~/MultipleAdditiveScenes/Scripts/PlayerScore.cs +++ b/Assets/Mirage/Samples~/MultipleAdditiveScenes/Scripts/PlayerScore.cs @@ -5,16 +5,16 @@ namespace Mirage.Examples.MultipleAdditiveScenes public class PlayerScore : NetworkBehaviour { [SyncVar] - public int playerNumber; + public int playerNumber { get; set; } [SyncVar] - public int scoreIndex; + public int scoreIndex { get; set; } [SyncVar] - public int matchIndex; + public int matchIndex { get; set; } [SyncVar] - public uint score; + public uint score { get; set; } public int clientMatchIndex = -1; diff --git a/Assets/Mirage/Samples~/MultipleAdditiveScenes/Scripts/RandomColor.cs b/Assets/Mirage/Samples~/MultipleAdditiveScenes/Scripts/RandomColor.cs index 2faf1c383d9..e1630dbd39b 100644 --- a/Assets/Mirage/Samples~/MultipleAdditiveScenes/Scripts/RandomColor.cs +++ b/Assets/Mirage/Samples~/MultipleAdditiveScenes/Scripts/RandomColor.cs @@ -11,7 +11,7 @@ public void OnStartServer() // Color32 packs to 4 bytes [SyncVar(hook = nameof(SetColor))] - public Color32 color = Color.black; + public Color32 color { get; set; } = Color.black; // Unity clones the material when GetComponent().material is called // Cache it here and destroy it in OnDestroy to prevent a memory leak diff --git a/Assets/Mirage/Samples~/SpawnCustomPlayer/CustomCharacterSpawner.cs b/Assets/Mirage/Samples~/SpawnCustomPlayer/CustomCharacterSpawner.cs index 84528fa6040..2e89f510a9d 100644 --- a/Assets/Mirage/Samples~/SpawnCustomPlayer/CustomCharacterSpawner.cs +++ b/Assets/Mirage/Samples~/SpawnCustomPlayer/CustomCharacterSpawner.cs @@ -93,9 +93,9 @@ private CustomCharacter GetPrefab(CreateMMOCharacterMessage msg) } public class CustomCharacter : NetworkBehaviour { - [SyncVar] public string PlayerName; - [SyncVar] public Color hairColor; - [SyncVar] public Color eyeColor; + [SyncVar] public string PlayerName { get; set; } + [SyncVar] public Color hairColor { get; set; } + [SyncVar] public Color eyeColor { get; set; } private void Awake() { diff --git a/Assets/Mirage/Samples~/Tanks/Scripts/Tank.cs b/Assets/Mirage/Samples~/Tanks/Scripts/Tank.cs index e4acc8acd80..afd527a504e 100644 --- a/Assets/Mirage/Samples~/Tanks/Scripts/Tank.cs +++ b/Assets/Mirage/Samples~/Tanks/Scripts/Tank.cs @@ -17,17 +17,16 @@ public class Tank : NetworkBehaviour public GameObject projectilePrefab; public Transform projectileMount; - [Header("Game Stats")] [SyncVar] - public int health; + public int health { get; set; } [SyncVar] - public int score; + public int score { get; set; } [SyncVar] - public string playerName; + public string playerName { get; set; } [SyncVar] - public bool allowMovement; + public bool allowMovement { get; set; } [SyncVar] - public bool isReady; + public bool isReady { get; set; } public bool IsDead => health <= 0; public TextMesh nameText; From 00f2f9c63fb7cd28b306dc8063b03fad7681d5c9 Mon Sep 17 00:00:00 2001 From: James Frowen Date: Sat, 30 May 2026 23:56:21 +0100 Subject: [PATCH 07/41] refactor: support properties in serialization attributes --- .../Runtime/Serialization/WeaverAttributes.cs | 17 +- .../Generated/BitCountFromRangeTests.meta | 8 - .../BitCountFromRange_MyByteEnum_0_3.cs | 174 --------------- .../BitCountFromRange_MyByteEnum_0_3.cs.meta | 11 - .../BitCountFromRange_MyDirection_N1_1.cs | 173 --------------- ...BitCountFromRange_MyDirection_N1_1.cs.meta | 11 - .../BitCountFromRange_int_N1000_0.cs | 167 -------------- .../BitCountFromRange_int_N1000_0.cs.meta | 11 - .../BitCountFromRange_int_N10_10.cs | 167 -------------- .../BitCountFromRange_int_N10_10.cs.meta | 11 - .../BitCountFromRange_int_N20000_20000.cs | 167 -------------- ...BitCountFromRange_int_N20000_20000.cs.meta | 11 - .../BitCountFromRange_int_N2000_N1000.cs | 167 -------------- .../BitCountFromRange_int_N2000_N1000.cs.meta | 11 - ...untFromRange_int_N2147483648_2147483647.cs | 167 -------------- ...omRange_int_N2147483648_2147483647.cs.meta | 11 - ...FromRange_int_N2147483648_2147483647max.cs | 167 -------------- ...ange_int_N2147483648_2147483647max.cs.meta | 11 - ...FromRange_int_N2147483648_2147483647min.cs | 167 -------------- ...ange_int_N2147483648_2147483647min.cs.meta | 11 - .../BitCountFromRange_short_N1000_1000.cs | 167 -------------- ...BitCountFromRange_short_N1000_1000.cs.meta | 11 - .../BitCountFromRange_short_N10_10.cs | 167 -------------- .../BitCountFromRange_short_N10_10.cs.meta | 11 - .../BitCountFromRange_short_N32768_32767.cs | 167 -------------- ...tCountFromRange_short_N32768_32767.cs.meta | 11 - .../BitCountFromRange_uint_0_5000.cs | 167 -------------- .../BitCountFromRange_uint_0_5000.cs.meta | 11 - .../BitCountFromRange_ushort_0_65535.cs | 167 -------------- .../BitCountFromRange_ushort_0_65535.cs.meta | 11 - Assets/Tests/Generated/BitCountTests.meta | 8 - .../BitCountBehaviour_MyByteEnum_4.cs | 175 --------------- .../BitCountBehaviour_MyByteEnum_4.cs.meta | 11 - .../BitCountBehaviour_MyEnum_4.cs | 175 --------------- .../BitCountBehaviour_MyEnum_4.cs.meta | 11 - .../BitCountTests/BitCountBehaviour_int_10.cs | 167 -------------- .../BitCountBehaviour_int_10.cs.meta | 11 - .../BitCountTests/BitCountBehaviour_int_17.cs | 167 -------------- .../BitCountBehaviour_int_17.cs.meta | 11 - .../BitCountTests/BitCountBehaviour_int_32.cs | 167 -------------- .../BitCountBehaviour_int_32.cs.meta | 11 - .../BitCountBehaviour_short_12.cs | 167 -------------- .../BitCountBehaviour_short_12.cs.meta | 11 - .../BitCountBehaviour_short_4.cs | 167 -------------- .../BitCountBehaviour_short_4.cs.meta | 11 - .../BitCountBehaviour_ulong_24.cs | 167 -------------- .../BitCountBehaviour_ulong_24.cs.meta | 11 - .../BitCountBehaviour_ulong_5.cs | 167 -------------- .../BitCountBehaviour_ulong_5.cs.meta | 11 - .../BitCountBehaviour_ulong_64.cs | 167 -------------- .../BitCountBehaviour_ulong_64.cs.meta | 11 - Assets/Tests/Generated/FloatPackTests.meta | 8 - .../FloatPackBehaviour_100_10.cs | 167 -------------- .../FloatPackBehaviour_100_10.cs.meta | 11 - .../FloatPackBehaviour_100_14.cs | 167 -------------- .../FloatPackBehaviour_100_14.cs.meta | 11 - .../FloatPackBehaviour_10_10.cs | 167 -------------- .../FloatPackBehaviour_10_10.cs.meta | 11 - .../FloatPackTests/FloatPackBehaviour_1_8.cs | 167 -------------- .../FloatPackBehaviour_1_8.cs.meta | 11 - .../FloatPackBehaviour_500_14.cs | 167 -------------- .../FloatPackBehaviour_500_14.cs.meta | 11 - .../FloatPackBehaviour_500_17.cs | 167 -------------- .../FloatPackBehaviour_500_17.cs.meta | 11 - .../Tests/Generated/QuaternionPackTests.meta | 8 - .../QuaternionPackBehaviour_10_0.cs | 178 --------------- .../QuaternionPackBehaviour_10_0.cs.meta | 11 - .../QuaternionPackBehaviour_10_45.cs | 178 --------------- .../QuaternionPackBehaviour_10_45.cs.meta | 11 - .../QuaternionPackBehaviour_8_0.cs | 178 --------------- .../QuaternionPackBehaviour_8_0.cs.meta | 11 - .../QuaternionPackBehaviour_8_45.cs | 178 --------------- .../QuaternionPackBehaviour_8_45.cs.meta | 11 - .../QuaternionPackBehaviour_9_0.cs | 178 --------------- .../QuaternionPackBehaviour_9_0.cs.meta | 11 - .../QuaternionPackBehaviour_9_45.cs | 178 --------------- .../QuaternionPackBehaviour_9_45.cs.meta | 11 - Assets/Tests/Generated/VarIntBlocksTests.meta | 8 - .../VarIntBlocksBehaviour_MyEnumByte_4.cs | 203 ------------------ ...VarIntBlocksBehaviour_MyEnumByte_4.cs.meta | 11 - .../VarIntBlocksBehaviour_MyEnum_4.cs | 203 ------------------ .../VarIntBlocksBehaviour_MyEnum_4.cs.meta | 11 - .../VarIntBlocksBehaviour_int_6.cs | 192 ----------------- .../VarIntBlocksBehaviour_int_6.cs.meta | 11 - .../VarIntBlocksBehaviour_int_7.cs | 191 ---------------- .../VarIntBlocksBehaviour_int_7.cs.meta | 11 - .../VarIntBlocksBehaviour_long_8.cs | 192 ----------------- .../VarIntBlocksBehaviour_long_8.cs.meta | 11 - .../VarIntBlocksBehaviour_short_6.cs | 192 ----------------- .../VarIntBlocksBehaviour_short_6.cs.meta | 11 - .../VarIntBlocksBehaviour_uint_6.cs | 192 ----------------- .../VarIntBlocksBehaviour_uint_6.cs.meta | 11 - .../VarIntBlocksBehaviour_uint_7.cs | 192 ----------------- .../VarIntBlocksBehaviour_uint_7.cs.meta | 11 - .../VarIntBlocksBehaviour_uint_8.cs | 193 ----------------- .../VarIntBlocksBehaviour_uint_8.cs.meta | 11 - .../VarIntBlocksBehaviour_ulong_9.cs | 192 ----------------- .../VarIntBlocksBehaviour_ulong_9.cs.meta | 11 - .../VarIntBlocksBehaviour_ushort_7.cs | 192 ----------------- .../VarIntBlocksBehaviour_ushort_7.cs.meta | 11 - Assets/Tests/Generated/VarIntTests.meta | 8 - .../VarIntBehaviour_MyEnumByte_4_64.cs | 203 ------------------ .../VarIntBehaviour_MyEnumByte_4_64.cs.meta | 11 - .../VarIntBehaviour_MyEnum_4_64.cs | 203 ------------------ .../VarIntBehaviour_MyEnum_4_64.cs.meta | 11 - .../VarIntBehaviour_int_100_1000.cs | 192 ----------------- .../VarIntBehaviour_int_100_1000.cs.meta | 11 - .../VarIntBehaviour_int_100_10000.cs | 191 ---------------- .../VarIntBehaviour_int_100_10000.cs.meta | 11 - .../VarIntBehaviour_long_100_1000.cs | 192 ----------------- .../VarIntBehaviour_long_100_1000.cs.meta | 11 - .../VarIntBehaviour_short_100_1000.cs | 192 ----------------- .../VarIntBehaviour_short_100_1000.cs.meta | 11 - .../VarIntBehaviour_uint_100_1000.cs | 192 ----------------- .../VarIntBehaviour_uint_100_1000.cs.meta | 11 - .../VarIntBehaviour_uint_255_64000.cs | 192 ----------------- .../VarIntBehaviour_uint_255_64000.cs.meta | 11 - .../VarIntBehaviour_uint_500_32000.cs | 193 ----------------- .../VarIntBehaviour_uint_500_32000.cs.meta | 11 - .../VarIntBehaviour_ulong_100_1000.cs | 192 ----------------- .../VarIntBehaviour_ulong_100_1000.cs.meta | 11 - .../VarIntBehaviour_ushort_100_1000.cs | 192 ----------------- .../VarIntBehaviour_ushort_100_1000.cs.meta | 11 - Assets/Tests/Generated/Vector2PackTests.meta | 8 - .../Vector2PackBehaviour_1000_27b2.cs | 173 --------------- .../Vector2PackBehaviour_1000_27b2.cs.meta | 11 - .../Vector2PackBehaviour_1000_27f.cs | 173 --------------- .../Vector2PackBehaviour_1000_27f.cs.meta | 11 - .../Vector2PackBehaviour_1000_27f2.cs | 173 --------------- .../Vector2PackBehaviour_1000_27f2.cs.meta | 11 - .../Vector2PackBehaviour_1000_30b.cs | 173 --------------- .../Vector2PackBehaviour_1000_30b.cs.meta | 11 - .../Vector2PackBehaviour_100_18b2.cs | 173 --------------- .../Vector2PackBehaviour_100_18b2.cs.meta | 11 - .../Vector2PackBehaviour_100_18f.cs | 173 --------------- .../Vector2PackBehaviour_100_18f.cs.meta | 11 - .../Vector2PackBehaviour_100_18f2.cs | 173 --------------- .../Vector2PackBehaviour_100_18f2.cs.meta | 11 - .../Vector2PackBehaviour_100_20b.cs | 173 --------------- .../Vector2PackBehaviour_100_20b.cs.meta | 11 - .../Vector2PackBehaviour_200_26b.cs | 173 --------------- .../Vector2PackBehaviour_200_26b.cs.meta | 11 - .../Vector2PackBehaviour_200_26b2.cs | 173 --------------- .../Vector2PackBehaviour_200_26b2.cs.meta | 11 - .../Vector2PackBehaviour_200_26f.cs | 173 --------------- .../Vector2PackBehaviour_200_26f.cs.meta | 11 - .../Vector2PackBehaviour_200_26f2.cs | 173 --------------- .../Vector2PackBehaviour_200_26f2.cs.meta | 11 - Assets/Tests/Generated/Vector3PackTests.meta | 8 - .../Vector3PackBehaviour_1000_42b3.cs | 176 --------------- .../Vector3PackBehaviour_1000_42b3.cs.meta | 11 - .../Vector3PackBehaviour_1000_42f.cs | 176 --------------- .../Vector3PackBehaviour_1000_42f.cs.meta | 11 - .../Vector3PackBehaviour_1000_42f3.cs | 176 --------------- .../Vector3PackBehaviour_1000_42f3.cs.meta | 11 - .../Vector3PackBehaviour_1000_45b.cs | 176 --------------- .../Vector3PackBehaviour_1000_45b.cs.meta | 11 - .../Vector3PackBehaviour_100_28b3.cs | 176 --------------- .../Vector3PackBehaviour_100_28b3.cs.meta | 11 - .../Vector3PackBehaviour_100_28f.cs | 176 --------------- .../Vector3PackBehaviour_100_28f.cs.meta | 11 - .../Vector3PackBehaviour_100_28f3.cs | 176 --------------- .../Vector3PackBehaviour_100_28f3.cs.meta | 11 - .../Vector3PackBehaviour_100_30b.cs | 176 --------------- .../Vector3PackBehaviour_100_30b.cs.meta | 11 - .../Vector3PackBehaviour_200_39b.cs | 176 --------------- .../Vector3PackBehaviour_200_39b.cs.meta | 11 - .../Vector3PackBehaviour_200_39b3.cs | 176 --------------- .../Vector3PackBehaviour_200_39b3.cs.meta | 11 - .../Vector3PackBehaviour_200_39f.cs | 176 --------------- .../Vector3PackBehaviour_200_39f.cs.meta | 11 - .../Vector3PackBehaviour_200_39f3.cs | 176 --------------- .../Vector3PackBehaviour_200_39f3.cs.meta | 11 - Assets/Tests/Generated/ZigZagTests.meta | 8 - .../ZigZagBehaviour_MyEnum2_4_negative.cs | 173 --------------- ...ZigZagBehaviour_MyEnum2_4_negative.cs.meta | 11 - .../ZigZagTests/ZigZagBehaviour_MyEnum_4.cs | 173 --------------- .../ZigZagBehaviour_MyEnum_4.cs.meta | 11 - .../ZigZagTests/ZigZagBehaviour_int_10.cs | 167 -------------- .../ZigZagBehaviour_int_10.cs.meta | 11 - .../ZigZagBehaviour_int_10_negative.cs | 167 -------------- .../ZigZagBehaviour_int_10_negative.cs.meta | 11 - .../ZigZagTests/ZigZagBehaviour_long_10.cs | 167 -------------- .../ZigZagBehaviour_long_10.cs.meta | 11 - .../ZigZagBehaviour_long_10_negative.cs | 167 -------------- .../ZigZagBehaviour_long_10_negative.cs.meta | 11 - .../ZigZagTests/ZigZagBehaviour_short_10.cs | 167 -------------- .../ZigZagBehaviour_short_10.cs.meta | 11 - .../ZigZagBehaviour_short_10_negative.cs | 167 -------------- .../ZigZagBehaviour_short_10_negative.cs.meta | 11 - .../Performance/Runtime/10K/Scripts/Health.cs | 2 +- .../Runtime/10KL/Scripts/Health.cs | 2 +- .../Scripts/MonsterBehavior.cs | 6 +- .../NetworkIdentityPerformance.cs | 2 +- .../Generics/WithGenericSyncVarEvent.cs | 2 +- .../Generics/WithGenericSyncVarHook.cs | 2 +- .../NetworkIdentitySyncvarTest.cs | 2 +- .../Runtime/ClientServer/SyncVarEventHook.cs | 2 +- .../SyncVarFireHookOnServerTests.cs | 4 +- .../SyncVarHooks/SyncVarHookArgCountTests.cs | 14 +- .../NetworkBehaviourSerializeTest.cs | 16 +- .../Syncing/SyncDirectionFromOwnerHook.cs | 8 +- .../SyncDirectionFromServerHostToOwner.cs | 6 +- .../Runtime/Syncing/SyncVarHookOrderTest.cs | 4 +- .../Syncing/SyncVarInitialStateFlagTest.cs | 2 +- .../Runtime/Syncing/SyncVarVirtualTest.cs | 4 +- 206 files changed, 49 insertions(+), 17019 deletions(-) delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests.meta delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyByteEnum_0_3.cs delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyByteEnum_0_3.cs.meta delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyDirection_N1_1.cs delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyDirection_N1_1.cs.meta delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N1000_0.cs delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N1000_0.cs.meta delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N10_10.cs delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N10_10.cs.meta delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N20000_20000.cs delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N20000_20000.cs.meta delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2000_N1000.cs delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2000_N1000.cs.meta delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647.cs delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647.cs.meta delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647max.cs delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647max.cs.meta delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647min.cs delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647min.cs.meta delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N1000_1000.cs delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N1000_1000.cs.meta delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N10_10.cs delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N10_10.cs.meta delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N32768_32767.cs delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N32768_32767.cs.meta delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_uint_0_5000.cs delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_uint_0_5000.cs.meta delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_ushort_0_65535.cs delete mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_ushort_0_65535.cs.meta delete mode 100644 Assets/Tests/Generated/BitCountTests.meta delete mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyByteEnum_4.cs delete mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyByteEnum_4.cs.meta delete mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyEnum_4.cs delete mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyEnum_4.cs.meta delete mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_10.cs delete mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_10.cs.meta delete mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_17.cs delete mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_17.cs.meta delete mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_32.cs delete mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_32.cs.meta delete mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_12.cs delete mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_12.cs.meta delete mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_4.cs delete mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_4.cs.meta delete mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_24.cs delete mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_24.cs.meta delete mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_5.cs delete mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_5.cs.meta delete mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_64.cs delete mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_64.cs.meta delete mode 100644 Assets/Tests/Generated/FloatPackTests.meta delete mode 100644 Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_10.cs delete mode 100644 Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_10.cs.meta delete mode 100644 Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_14.cs delete mode 100644 Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_14.cs.meta delete mode 100644 Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_10_10.cs delete mode 100644 Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_10_10.cs.meta delete mode 100644 Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_1_8.cs delete mode 100644 Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_1_8.cs.meta delete mode 100644 Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_14.cs delete mode 100644 Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_14.cs.meta delete mode 100644 Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_17.cs delete mode 100644 Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_17.cs.meta delete mode 100644 Assets/Tests/Generated/QuaternionPackTests.meta delete mode 100644 Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_0.cs delete mode 100644 Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_0.cs.meta delete mode 100644 Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_45.cs delete mode 100644 Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_45.cs.meta delete mode 100644 Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_0.cs delete mode 100644 Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_0.cs.meta delete mode 100644 Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_45.cs delete mode 100644 Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_45.cs.meta delete mode 100644 Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_0.cs delete mode 100644 Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_0.cs.meta delete mode 100644 Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_45.cs delete mode 100644 Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_45.cs.meta delete mode 100644 Assets/Tests/Generated/VarIntBlocksTests.meta delete mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnumByte_4.cs delete mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnumByte_4.cs.meta delete mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnum_4.cs delete mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnum_4.cs.meta delete mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_6.cs delete mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_6.cs.meta delete mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_7.cs delete mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_7.cs.meta delete mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_long_8.cs delete mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_long_8.cs.meta delete mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_short_6.cs delete mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_short_6.cs.meta delete mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_6.cs delete mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_6.cs.meta delete mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_7.cs delete mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_7.cs.meta delete mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_8.cs delete mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_8.cs.meta delete mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ulong_9.cs delete mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ulong_9.cs.meta delete mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ushort_7.cs delete mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ushort_7.cs.meta delete mode 100644 Assets/Tests/Generated/VarIntTests.meta delete mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnumByte_4_64.cs delete mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnumByte_4_64.cs.meta delete mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnum_4_64.cs delete mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnum_4_64.cs.meta delete mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_1000.cs delete mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_1000.cs.meta delete mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_10000.cs delete mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_10000.cs.meta delete mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_long_100_1000.cs delete mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_long_100_1000.cs.meta delete mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_short_100_1000.cs delete mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_short_100_1000.cs.meta delete mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_100_1000.cs delete mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_100_1000.cs.meta delete mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_255_64000.cs delete mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_255_64000.cs.meta delete mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_500_32000.cs delete mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_500_32000.cs.meta delete mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ulong_100_1000.cs delete mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ulong_100_1000.cs.meta delete mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ushort_100_1000.cs delete mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ushort_100_1000.cs.meta delete mode 100644 Assets/Tests/Generated/Vector2PackTests.meta delete mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27b2.cs delete mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27b2.cs.meta delete mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f.cs delete mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f.cs.meta delete mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f2.cs delete mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f2.cs.meta delete mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_30b.cs delete mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_30b.cs.meta delete mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18b2.cs delete mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18b2.cs.meta delete mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f.cs delete mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f.cs.meta delete mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f2.cs delete mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f2.cs.meta delete mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_20b.cs delete mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_20b.cs.meta delete mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b.cs delete mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b.cs.meta delete mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b2.cs delete mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b2.cs.meta delete mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f.cs delete mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f.cs.meta delete mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f2.cs delete mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f2.cs.meta delete mode 100644 Assets/Tests/Generated/Vector3PackTests.meta delete mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42b3.cs delete mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42b3.cs.meta delete mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f.cs delete mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f.cs.meta delete mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f3.cs delete mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f3.cs.meta delete mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_45b.cs delete mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_45b.cs.meta delete mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28b3.cs delete mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28b3.cs.meta delete mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f.cs delete mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f.cs.meta delete mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f3.cs delete mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f3.cs.meta delete mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_30b.cs delete mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_30b.cs.meta delete mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b.cs delete mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b.cs.meta delete mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b3.cs delete mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b3.cs.meta delete mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f.cs delete mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f.cs.meta delete mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f3.cs delete mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f3.cs.meta delete mode 100644 Assets/Tests/Generated/ZigZagTests.meta delete mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum2_4_negative.cs delete mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum2_4_negative.cs.meta delete mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum_4.cs delete mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum_4.cs.meta delete mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10.cs delete mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10.cs.meta delete mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10_negative.cs delete mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10_negative.cs.meta delete mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10.cs delete mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10.cs.meta delete mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10_negative.cs delete mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10_negative.cs.meta delete mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10.cs delete mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10.cs.meta delete mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10_negative.cs delete mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10_negative.cs.meta diff --git a/Assets/Mirage/Runtime/Serialization/WeaverAttributes.cs b/Assets/Mirage/Runtime/Serialization/WeaverAttributes.cs index b21a9dc8fef..7488ffe7ffd 100644 --- a/Assets/Mirage/Runtime/Serialization/WeaverAttributes.cs +++ b/Assets/Mirage/Runtime/Serialization/WeaverAttributes.cs @@ -8,7 +8,7 @@ namespace Mirage.Serialization /// /// Tells Weaver to ignore a field or Method /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] public sealed class WeaverIgnoreAttribute : Attribute { } /// @@ -43,7 +43,7 @@ public WeaverSerializeCollectionAttribute() { } /// /// Also See: Bit Packing Documentation /// - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter)] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)] public class BitCountAttribute : Attribute { /// Value should be between 1 and 64 @@ -54,7 +54,7 @@ public BitCountAttribute(int bitCount) { } /// Calculates bitcount from then given min/max values and then packs using /// Also See: Bit Packing Documentation /// - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter)] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)] public class BitCountFromRangeAttribute : Attribute { /// minimum possible int value @@ -66,7 +66,7 @@ public BitCountFromRangeAttribute(int min, int max) { } /// Used along size to encodes a integer value using so that both positive and negative values can be sent /// Also See: Bit Packing Documentation /// - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter)] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)] public class ZigZagEncodeAttribute : Attribute { public ZigZagEncodeAttribute() { } @@ -76,7 +76,7 @@ public ZigZagEncodeAttribute() { } /// Packs a float field, clamped from -max to +max, with /// Also See: Bit Packing Documentation /// - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter)] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)] public class FloatPackAttribute : Attribute { /// Max value of the float @@ -91,6 +91,7 @@ public FloatPackAttribute(float max, int bitCount) { } /// /// /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)] public class Vector3PackAttribute : Attribute { public Vector3PackAttribute(float xMax, float yMax, float zMax, float xPrecision, float yPrecision, float zPrecision) { } @@ -103,6 +104,7 @@ public Vector3PackAttribute(float xMax, float yMax, float zMax, int bitCount) { /// /// /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)] public class Vector2PackAttribute : Attribute { public Vector2PackAttribute(float xMax, float yMax, float xPrecision, float yPrecision) { } @@ -115,6 +117,7 @@ public Vector2PackAttribute(float xMax, float yMax, int bitCount) { } /// /// /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)] public class QuaternionPackAttribute : Attribute { public QuaternionPackAttribute(int bitPerElement = 9) { } @@ -125,7 +128,7 @@ public QuaternionPackAttribute(int bitPerElement = 9) { } /// Allows small values to be sent using less bits /// Only works with integer fields (byte, int, ulong, enums etc) /// - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter)] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)] public class VarIntAttribute : Attribute { public VarIntAttribute(ulong smallMax, ulong mediumMax) { } @@ -139,7 +142,7 @@ public VarIntAttribute(ulong smallMax, ulong mediumMax, ulong largeMax, bool thr /// Allows small values to be sent using less bits /// Only works with integer fields (byte, int, ulong, enums etc) /// - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter)] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)] public class VarIntBlocksAttribute : Attribute { /// diff --git a/Assets/Tests/Generated/BitCountFromRangeTests.meta b/Assets/Tests/Generated/BitCountFromRangeTests.meta deleted file mode 100644 index 1992c1d0462..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 8cb64a68508b9124e83b82d7b32e82c9 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyByteEnum_0_3.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyByteEnum_0_3.cs deleted file mode 100644 index 180410a1587..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyByteEnum_0_3.cs +++ /dev/null @@ -1,174 +0,0 @@ -// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.MyByteEnum_0_3 -{ - [System.Serializable] - public enum MyByteEnum : byte - { - None = 0, - Slow = 1, - Fast = 2, - ReallyFast = 3, - } - public class BitPackBehaviour : NetworkBehaviour - { - [BitCountFromRange(0, 3)] - [SyncVar] public MyByteEnum myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCountFromRange(0, 3)] MyByteEnum myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCountFromRange(0, 3)] - public MyByteEnum myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCountFromRange(0, 3)] - public MyByteEnum myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const MyByteEnum value = (MyByteEnum)3; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(2)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(2)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (2 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"2 bits is 1 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((2 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"2 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(2)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(2)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyByteEnum_0_3.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyByteEnum_0_3.cs.meta deleted file mode 100644 index b95cf818a93..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyByteEnum_0_3.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: f0ea6f23680a449479ca061794962e59 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyDirection_N1_1.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyDirection_N1_1.cs deleted file mode 100644 index fc284865698..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyDirection_N1_1.cs +++ /dev/null @@ -1,173 +0,0 @@ -// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.MyDirection_N1_1 -{ - [System.Serializable] - public enum MyDirection - { - Left = -1, - None = 0, - Right = 1, - } - public class BitPackBehaviour : NetworkBehaviour - { - [BitCountFromRange(-1, 1)] - [SyncVar] public MyDirection myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCountFromRange(-1, 1)] MyDirection myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCountFromRange(-1, 1)] - public MyDirection myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCountFromRange(-1, 1)] - public MyDirection myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const MyDirection value = (MyDirection)1; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(2)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(2)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (2 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"2 bits is 1 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((2 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"2 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(2)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(2)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyDirection_N1_1.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyDirection_N1_1.cs.meta deleted file mode 100644 index f69e69994b7..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyDirection_N1_1.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 2654c2521316ddd4a98e17939c9cdcd8 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N1000_0.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N1000_0.cs deleted file mode 100644 index 35cba979876..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N1000_0.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.int_N1000_0 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [BitCountFromRange(-1000, 0)] - [SyncVar] public int myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCountFromRange(-1000, 0)] int myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCountFromRange(-1000, 0)] - public int myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCountFromRange(-1000, 0)] - public int myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const int value = -3; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(10)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(10)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (10 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is 2 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((10 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(10)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(10)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N1000_0.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N1000_0.cs.meta deleted file mode 100644 index d00037efb26..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N1000_0.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: e7737807c8d046b439d43617646710d8 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N10_10.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N10_10.cs deleted file mode 100644 index b0b6a472b89..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N10_10.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.int_N10_10 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [BitCountFromRange(-10, 10)] - [SyncVar] public int myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCountFromRange(-10, 10)] int myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCountFromRange(-10, 10)] - public int myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCountFromRange(-10, 10)] - public int myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const int value = -3; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(5)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(5)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (5 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"5 bits is 1 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((5 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"5 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(5)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(5)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N10_10.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N10_10.cs.meta deleted file mode 100644 index 04c8e5e9cc5..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N10_10.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 59d7a4e915271844488f13295fc7a8f3 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N20000_20000.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N20000_20000.cs deleted file mode 100644 index 80dd6792f63..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N20000_20000.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.int_N20000_20000 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [BitCountFromRange(-20000, 20000)] - [SyncVar] public int myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCountFromRange(-20000, 20000)] int myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCountFromRange(-20000, 20000)] - public int myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCountFromRange(-20000, 20000)] - public int myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const int value = 20; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(16)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(16)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (16 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"16 bits is 2 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((16 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"16 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(16)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(16)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N20000_20000.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N20000_20000.cs.meta deleted file mode 100644 index ea2663d1130..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N20000_20000.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 24db0dadb6cdcff4c86a6b891b96c4f4 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2000_N1000.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2000_N1000.cs deleted file mode 100644 index 21f549d53f7..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2000_N1000.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.int_N2000_N1000 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [BitCountFromRange(-2000, -1000)] - [SyncVar] public int myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCountFromRange(-2000, -1000)] int myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCountFromRange(-2000, -1000)] - public int myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCountFromRange(-2000, -1000)] - public int myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const int value = -1400; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(10)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(10)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (10 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is 2 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((10 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(10)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(10)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2000_N1000.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2000_N1000.cs.meta deleted file mode 100644 index 5a5bfee54cf..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2000_N1000.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: c5a1e350102a4064792c236943e12b32 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647.cs deleted file mode 100644 index 71be3e9e82c..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.int_N2147483648_2147483647 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [BitCountFromRange(-2147483648, 2147483647)] - [SyncVar] public int myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCountFromRange(-2147483648, 2147483647)] int myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCountFromRange(-2147483648, 2147483647)] - public int myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCountFromRange(-2147483648, 2147483647)] - public int myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const int value = 20; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(32)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(32)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (32 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"32 bits is 4 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((32 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"32 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(32)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(32)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647.cs.meta deleted file mode 100644 index c078b8e8650..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 5890a0f5c296a564fb2ab28ba115a4df -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647max.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647max.cs deleted file mode 100644 index 7e4786ef8ad..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647max.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.int_N2147483648_2147483647max -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [BitCountFromRange(-2147483648, 2147483647)] - [SyncVar] public int myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCountFromRange(-2147483648, 2147483647)] int myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCountFromRange(-2147483648, 2147483647)] - public int myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCountFromRange(-2147483648, 2147483647)] - public int myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const int value = 2147483647; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(32)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(32)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (32 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"32 bits is 4 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((32 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"32 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(32)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(32)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647max.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647max.cs.meta deleted file mode 100644 index 9cb26d9a380..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647max.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 23989d597941a43468cdbe44a0462e5f -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647min.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647min.cs deleted file mode 100644 index 9ed311814e0..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647min.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.int_N2147483648_2147483647min -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [BitCountFromRange(-2147483648, 2147483647)] - [SyncVar] public int myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCountFromRange(-2147483648, 2147483647)] int myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCountFromRange(-2147483648, 2147483647)] - public int myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCountFromRange(-2147483648, 2147483647)] - public int myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const int value = -2147483648; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(32)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(32)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (32 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"32 bits is 4 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((32 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"32 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(32)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(32)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647min.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647min.cs.meta deleted file mode 100644 index 810792e817a..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647min.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: fbc3722634dcb374eb7fade5be726c0b -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N1000_1000.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N1000_1000.cs deleted file mode 100644 index 1aa0618ca8f..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N1000_1000.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.short_N1000_1000 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [BitCountFromRange(-1000, 1000)] - [SyncVar] public short myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCountFromRange(-1000, 1000)] short myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCountFromRange(-1000, 1000)] - public short myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCountFromRange(-1000, 1000)] - public short myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const short value = 20; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(11)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(11)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (11 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"11 bits is 2 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((11 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"11 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(11)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(11)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N1000_1000.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N1000_1000.cs.meta deleted file mode 100644 index 11abfc7d15f..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N1000_1000.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 043ee658c5663f941b9bdbe629203904 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N10_10.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N10_10.cs deleted file mode 100644 index 2bc3e490295..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N10_10.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.short_N10_10 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [BitCountFromRange(-10, 10)] - [SyncVar] public short myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCountFromRange(-10, 10)] short myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCountFromRange(-10, 10)] - public short myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCountFromRange(-10, 10)] - public short myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const short value = 3; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(5)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(5)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (5 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"5 bits is 1 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((5 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"5 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(5)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(5)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N10_10.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N10_10.cs.meta deleted file mode 100644 index 99897962e41..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N10_10.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 6db00fed3a6c6db479014d4fb0da8bfa -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N32768_32767.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N32768_32767.cs deleted file mode 100644 index b3698e12c1c..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N32768_32767.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.short_N32768_32767 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [BitCountFromRange(-32768, 32767)] - [SyncVar] public short myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCountFromRange(-32768, 32767)] short myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCountFromRange(-32768, 32767)] - public short myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCountFromRange(-32768, 32767)] - public short myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const short value = 20; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(16)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(16)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (16 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"16 bits is 2 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((16 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"16 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(16)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(16)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N32768_32767.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N32768_32767.cs.meta deleted file mode 100644 index ab02b553d93..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N32768_32767.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: b199c8868e0116a4f80bbac71aa09a44 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_uint_0_5000.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_uint_0_5000.cs deleted file mode 100644 index 0563d079648..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_uint_0_5000.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.uint_0_5000 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [BitCountFromRange(0, 5000)] - [SyncVar] public uint myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCountFromRange(0, 5000)] uint myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCountFromRange(0, 5000)] - public uint myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCountFromRange(0, 5000)] - public uint myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const uint value = 20; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(13)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(13)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (13 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"13 bits is 2 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((13 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"13 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(13)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(13)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_uint_0_5000.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_uint_0_5000.cs.meta deleted file mode 100644 index f48241b4b15..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_uint_0_5000.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: fc1514bc9e72a8b4a95552437de454a2 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_ushort_0_65535.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_ushort_0_65535.cs deleted file mode 100644 index 47d05a81499..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_ushort_0_65535.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.ushort_0_65535 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [BitCountFromRange(0, 65535)] - [SyncVar] public ushort myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCountFromRange(0, 65535)] ushort myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCountFromRange(0, 65535)] - public ushort myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCountFromRange(0, 65535)] - public ushort myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const ushort value = 20; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(16)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(16)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (16 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"16 bits is 2 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((16 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"16 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(16)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(16)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_ushort_0_65535.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_ushort_0_65535.cs.meta deleted file mode 100644 index fc90c8300cf..00000000000 --- a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_ushort_0_65535.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: ac04d5e2e5d065d479de23438930b877 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountTests.meta b/Assets/Tests/Generated/BitCountTests.meta deleted file mode 100644 index 706e5c13295..00000000000 --- a/Assets/Tests/Generated/BitCountTests.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: d467a8a2dda82ad4a8133a09f6ffac51 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyByteEnum_4.cs b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyByteEnum_4.cs deleted file mode 100644 index 2ee2fe0c424..00000000000 --- a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyByteEnum_4.cs +++ /dev/null @@ -1,175 +0,0 @@ -// DO NOT EDIT: GENERATED BY BitCountTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.BitCountAttributeTests.MyByteEnum_4 -{ - [System.Flags, System.Serializable] - public enum MyByteEnum : byte - { - None = 0, - HasHealth = 1, - HasArmor = 2, - HasGun = 4, - HasAmmo = 8, - } - public class BitPackBehaviour : NetworkBehaviour - { - [BitCount(4)] - [SyncVar] public MyByteEnum myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCount(4)] MyByteEnum myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCount(4)] - public MyByteEnum myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCount(4)] - public MyByteEnum myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const MyByteEnum value = (MyByteEnum)3; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(4)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(4)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (4 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"4 bits is 1 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((4 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"4 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(4)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(4)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyByteEnum_4.cs.meta b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyByteEnum_4.cs.meta deleted file mode 100644 index d8b85647df5..00000000000 --- a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyByteEnum_4.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 07a82e15515c61743a462a884a5e0c58 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyEnum_4.cs b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyEnum_4.cs deleted file mode 100644 index 9bc650871cb..00000000000 --- a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyEnum_4.cs +++ /dev/null @@ -1,175 +0,0 @@ -// DO NOT EDIT: GENERATED BY BitCountTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.BitCountAttributeTests.MyEnum_4 -{ - [System.Flags, System.Serializable] - public enum MyEnum - { - None = 0, - HasHealth = 1, - HasArmor = 2, - HasGun = 4, - HasAmmo = 8, - } - public class BitPackBehaviour : NetworkBehaviour - { - [BitCount(4)] - [SyncVar] public MyEnum myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCount(4)] MyEnum myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCount(4)] - public MyEnum myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCount(4)] - public MyEnum myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const MyEnum value = (MyEnum)3; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(4)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(4)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (4 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"4 bits is 1 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((4 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"4 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(4)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(4)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyEnum_4.cs.meta b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyEnum_4.cs.meta deleted file mode 100644 index 417ba6194ea..00000000000 --- a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyEnum_4.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: a88893ffaef084b45acf2820fb664975 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_10.cs b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_10.cs deleted file mode 100644 index 7ccc96ac07f..00000000000 --- a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_10.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY BitCountTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.BitCountAttributeTests.int_10 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [BitCount(10)] - [SyncVar] public int myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCount(10)] int myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCount(10)] - public int myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCount(10)] - public int myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const int value = 20; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(10)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(10)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (10 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is 2 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((10 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(10)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(10)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_10.cs.meta b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_10.cs.meta deleted file mode 100644 index 468cbc7af03..00000000000 --- a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_10.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 35eb226fb46a6194a948d714de75dc54 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_17.cs b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_17.cs deleted file mode 100644 index 2a9eb482d95..00000000000 --- a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_17.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY BitCountTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.BitCountAttributeTests.int_17 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [BitCount(17)] - [SyncVar] public int myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCount(17)] int myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCount(17)] - public int myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCount(17)] - public int myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const int value = 20; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(17)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(17)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (17 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"17 bits is 3 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((17 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"17 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(17)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(17)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_17.cs.meta b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_17.cs.meta deleted file mode 100644 index 96a2c3ba411..00000000000 --- a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_17.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: c88ea0247a9d41043b6247338c9eca4d -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_32.cs b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_32.cs deleted file mode 100644 index 2af078e613e..00000000000 --- a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_32.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY BitCountTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.BitCountAttributeTests.int_32 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [BitCount(32)] - [SyncVar] public int myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCount(32)] int myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCount(32)] - public int myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCount(32)] - public int myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const int value = 20; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(32)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(32)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (32 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"32 bits is 4 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((32 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"32 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(32)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(32)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_32.cs.meta b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_32.cs.meta deleted file mode 100644 index ea77a629c26..00000000000 --- a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_32.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 6e3bb7a2ea9da0846a7ce769f31e1b3c -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_12.cs b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_12.cs deleted file mode 100644 index e79206b2638..00000000000 --- a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_12.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY BitCountTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.BitCountAttributeTests.short_12 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [BitCount(12)] - [SyncVar] public short myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCount(12)] short myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCount(12)] - public short myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCount(12)] - public short myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const short value = 20; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(12)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(12)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (12 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"12 bits is 2 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((12 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"12 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(12)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(12)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_12.cs.meta b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_12.cs.meta deleted file mode 100644 index fc4fd397a86..00000000000 --- a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_12.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 445320b56bb07754cb55b25514abf4d4 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_4.cs b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_4.cs deleted file mode 100644 index 7e1d1bb0652..00000000000 --- a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_4.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY BitCountTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.BitCountAttributeTests.short_4 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [BitCount(4)] - [SyncVar] public short myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCount(4)] short myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCount(4)] - public short myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCount(4)] - public short myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const short value = 3; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(4)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(4)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (4 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"4 bits is 1 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((4 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"4 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(4)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(4)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_4.cs.meta b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_4.cs.meta deleted file mode 100644 index e2594abe8a6..00000000000 --- a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_4.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 66b0359ad03bbbd438468152ea395144 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_24.cs b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_24.cs deleted file mode 100644 index 1a52f577748..00000000000 --- a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_24.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY BitCountTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.BitCountAttributeTests.ulong_24 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [BitCount(24)] - [SyncVar] public ulong myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCount(24)] ulong myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCount(24)] - public ulong myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCount(24)] - public ulong myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const ulong value = 20; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(24)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(24)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (24 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"24 bits is 3 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((24 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"24 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(24)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(24)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_24.cs.meta b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_24.cs.meta deleted file mode 100644 index 288ac5a1091..00000000000 --- a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_24.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: e11a7f817944ccc4cb4bcf80be0e8bfb -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_5.cs b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_5.cs deleted file mode 100644 index 7aedde458e6..00000000000 --- a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_5.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY BitCountTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.BitCountAttributeTests.ulong_5 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [BitCount(5)] - [SyncVar] public ulong myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCount(5)] ulong myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCount(5)] - public ulong myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCount(5)] - public ulong myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const ulong value = 20; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(5)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(5)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (5 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"5 bits is 1 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((5 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"5 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(5)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(5)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_5.cs.meta b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_5.cs.meta deleted file mode 100644 index 1a9bbe6a8dd..00000000000 --- a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_5.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 46af30f0b26070a40a79fb8bcd03a987 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_64.cs b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_64.cs deleted file mode 100644 index 7b9d67fb373..00000000000 --- a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_64.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY BitCountTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.BitCountAttributeTests.ulong_64 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [BitCount(64)] - [SyncVar] public ulong myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCount(64)] ulong myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCount(64)] - public ulong myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCount(64)] - public ulong myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const ulong value = 20; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(64)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(64)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (64 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"64 bits is 8 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((64 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"64 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(64)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(64)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_64.cs.meta b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_64.cs.meta deleted file mode 100644 index 1fae091dbaa..00000000000 --- a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_64.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 20adbacec1c8df644bc7f00d9ca44f2d -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/FloatPackTests.meta b/Assets/Tests/Generated/FloatPackTests.meta deleted file mode 100644 index 7894b920595..00000000000 --- a/Assets/Tests/Generated/FloatPackTests.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 26357b638cc57df4faada16fce93ea05 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_10.cs b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_10.cs deleted file mode 100644 index acbd004a80d..00000000000 --- a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_10.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY FloatPackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.FloatPackAttributeTests._100_10 -{ - public class BitPackBehaviour : NetworkBehaviour - { - [FloatPack(100, 0.2f)] - [SyncVar] public float myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([FloatPack(100, 0.2f)] float myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [FloatPack(100, 0.2f)] - public float myValue; - } - - [Serializable] - public struct BitPackStruct - { - [FloatPack(100, 0.2f)] - public float myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const float value = 5.2f; - private const float within = 0.196f; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(10)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(10)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value).Within(within)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value).Within(within)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (10 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((10 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage.myValue, Is.EqualTo(inMessage.myValue).Within(within)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(10)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(10)); - - Assert.That(outStruct.myValue, Is.EqualTo(inStruct.myValue).Within(within)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_10.cs.meta b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_10.cs.meta deleted file mode 100644 index ac255a646f8..00000000000 --- a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_10.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: ad89b419431e6004ca48c95535ff2500 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_14.cs b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_14.cs deleted file mode 100644 index 86936e232dc..00000000000 --- a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_14.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY FloatPackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.FloatPackAttributeTests._100_14 -{ - public class BitPackBehaviour : NetworkBehaviour - { - [FloatPack(100, 0.02f)] - [SyncVar] public float myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([FloatPack(100, 0.02f)] float myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [FloatPack(100, 0.02f)] - public float myValue; - } - - [Serializable] - public struct BitPackStruct - { - [FloatPack(100, 0.02f)] - public float myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const float value = 5.2f; - private const float within = 0.0123f; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(14)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(14)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value).Within(within)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value).Within(within)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (14 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"14 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((14 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"14 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage.myValue, Is.EqualTo(inMessage.myValue).Within(within)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(14)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(14)); - - Assert.That(outStruct.myValue, Is.EqualTo(inStruct.myValue).Within(within)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_14.cs.meta b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_14.cs.meta deleted file mode 100644 index b7f8135c4a0..00000000000 --- a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_14.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 016ea61e12913554fbceaf553a970d39 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_10_10.cs b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_10_10.cs deleted file mode 100644 index 7ac987f3a06..00000000000 --- a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_10_10.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY FloatPackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.FloatPackAttributeTests._10_10 -{ - public class BitPackBehaviour : NetworkBehaviour - { - [FloatPack(10, 10)] - [SyncVar] public float myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([FloatPack(10, 10)] float myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [FloatPack(10, 10)] - public float myValue; - } - - [Serializable] - public struct BitPackStruct - { - [FloatPack(10, 10)] - public float myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const float value = 3.3f; - private const float within = 0.0191f; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(10)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(10)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value).Within(within)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value).Within(within)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (10 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((10 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage.myValue, Is.EqualTo(inMessage.myValue).Within(within)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(10)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(10)); - - Assert.That(outStruct.myValue, Is.EqualTo(inStruct.myValue).Within(within)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_10_10.cs.meta b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_10_10.cs.meta deleted file mode 100644 index 1c0f4e16147..00000000000 --- a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_10_10.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 3736f9e65388d0a4dad0c506535044a4 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_1_8.cs b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_1_8.cs deleted file mode 100644 index 0f386513b9d..00000000000 --- a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_1_8.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY FloatPackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.FloatPackAttributeTests._1_8 -{ - public class BitPackBehaviour : NetworkBehaviour - { - [FloatPack(1, 8)] - [SyncVar] public float myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([FloatPack(1, 8)] float myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [FloatPack(1, 8)] - public float myValue; - } - - [Serializable] - public struct BitPackStruct - { - [FloatPack(1, 8)] - public float myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const float value = 0.2f; - private const float within = 0.00785f; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(8)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(8)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value).Within(within)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value).Within(within)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (8 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"8 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((8 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"8 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage.myValue, Is.EqualTo(inMessage.myValue).Within(within)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(8)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(8)); - - Assert.That(outStruct.myValue, Is.EqualTo(inStruct.myValue).Within(within)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_1_8.cs.meta b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_1_8.cs.meta deleted file mode 100644 index 0733489db2e..00000000000 --- a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_1_8.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 689399c2254fca041840b7084462a871 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_14.cs b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_14.cs deleted file mode 100644 index e6ae75fb09a..00000000000 --- a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_14.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY FloatPackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.FloatPackAttributeTests._500_14 -{ - public class BitPackBehaviour : NetworkBehaviour - { - [FloatPack(500, 0.1f)] - [SyncVar] public float myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([FloatPack(500, 0.1f)] float myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [FloatPack(500, 0.1f)] - public float myValue; - } - - [Serializable] - public struct BitPackStruct - { - [FloatPack(500, 0.1f)] - public float myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const float value = 5.2f; - private const float within = 0.0123f; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(14)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(14)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value).Within(within)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value).Within(within)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (14 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"14 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((14 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"14 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage.myValue, Is.EqualTo(inMessage.myValue).Within(within)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(14)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(14)); - - Assert.That(outStruct.myValue, Is.EqualTo(inStruct.myValue).Within(within)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_14.cs.meta b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_14.cs.meta deleted file mode 100644 index 48629925d93..00000000000 --- a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_14.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: d6364115de1c8e541b3e0ff8371fb856 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_17.cs b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_17.cs deleted file mode 100644 index 2c72b9caa04..00000000000 --- a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_17.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY FloatPackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.FloatPackAttributeTests._500_17 -{ - public class BitPackBehaviour : NetworkBehaviour - { - [FloatPack(500, 0.01f)] - [SyncVar] public float myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([FloatPack(500, 0.01f)] float myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [FloatPack(500, 0.01f)] - public float myValue; - } - - [Serializable] - public struct BitPackStruct - { - [FloatPack(500, 0.01f)] - public float myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const float value = 5.2f; - private const float within = 0.00763f; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(17)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(17)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value).Within(within)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value).Within(within)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (17 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"17 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((17 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"17 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage.myValue, Is.EqualTo(inMessage.myValue).Within(within)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(17)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(17)); - - Assert.That(outStruct.myValue, Is.EqualTo(inStruct.myValue).Within(within)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_17.cs.meta b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_17.cs.meta deleted file mode 100644 index b279434cf5f..00000000000 --- a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_17.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 38ae51883e12f624d891bd79db46c359 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/QuaternionPackTests.meta b/Assets/Tests/Generated/QuaternionPackTests.meta deleted file mode 100644 index c6fdc1af174..00000000000 --- a/Assets/Tests/Generated/QuaternionPackTests.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: d7bca41a46502324099c2f7c0bc703f2 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_0.cs b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_0.cs deleted file mode 100644 index f36d5d5bef6..00000000000 --- a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_0.cs +++ /dev/null @@ -1,178 +0,0 @@ -// DO NOT EDIT: GENERATED BY QuaternionPackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.QuaternionPackAttributeTests._10_0 -{ - public class BitPackBehaviour : NetworkBehaviour - { - [QuaternionPack(10)] - [SyncVar] public Quaternion myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([QuaternionPack(10)] Quaternion myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [QuaternionPack(10)] - public Quaternion myValue; - } - - [Serializable] - public struct BitPackStruct - { - [QuaternionPack(10)] - public Quaternion myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Quaternion value = new Quaternion(0f, 0.7071068f, 0f, 0.7071068f); - private const float within = 0.00135f; - - private static void AssertValue(Quaternion actual) - { - Vector3 inVec = value * Vector3.forward; - Vector3 outVec = actual * Vector3.forward; - - // allow for extra within when rotating vector - Assert.AreEqual(inVec.x, outVec.x, within * 2, $"vx off by {Mathf.Abs(inVec.x - outVec.x)}"); - Assert.AreEqual(inVec.y, outVec.y, within * 2, $"vy off by {Mathf.Abs(inVec.y - outVec.y)}"); - Assert.AreEqual(inVec.z, outVec.z, within * 2, $"vz off by {Mathf.Abs(inVec.z - outVec.z)}"); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(32)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(32)); - - AssertValue(clientComponent.myValue); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (32 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"32 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((32 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"32 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(32)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(32)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_0.cs.meta b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_0.cs.meta deleted file mode 100644 index c5dc8fb5daf..00000000000 --- a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_0.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 4ab9fd8937a174e4bb517b25a6feaea1 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_45.cs b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_45.cs deleted file mode 100644 index 6e880e850e2..00000000000 --- a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_45.cs +++ /dev/null @@ -1,178 +0,0 @@ -// DO NOT EDIT: GENERATED BY QuaternionPackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.QuaternionPackAttributeTests._10_45 -{ - public class BitPackBehaviour : NetworkBehaviour - { - [QuaternionPack(10)] - [SyncVar] public Quaternion myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([QuaternionPack(10)] Quaternion myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [QuaternionPack(10)] - public Quaternion myValue; - } - - [Serializable] - public struct BitPackStruct - { - [QuaternionPack(10)] - public Quaternion myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Quaternion value = new Quaternion(0.2705981f, 0.6532815f, -0.2705981f, 0.6532815f); - private const float within = 0.00135f; - - private static void AssertValue(Quaternion actual) - { - Vector3 inVec = value * Vector3.forward; - Vector3 outVec = actual * Vector3.forward; - - // allow for extra within when rotating vector - Assert.AreEqual(inVec.x, outVec.x, within * 2, $"vx off by {Mathf.Abs(inVec.x - outVec.x)}"); - Assert.AreEqual(inVec.y, outVec.y, within * 2, $"vy off by {Mathf.Abs(inVec.y - outVec.y)}"); - Assert.AreEqual(inVec.z, outVec.z, within * 2, $"vz off by {Mathf.Abs(inVec.z - outVec.z)}"); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(32)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(32)); - - AssertValue(clientComponent.myValue); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (32 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"32 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((32 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"32 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(32)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(32)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_45.cs.meta b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_45.cs.meta deleted file mode 100644 index 33c2db1a7f6..00000000000 --- a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_45.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 2aae041c7c6f09e41943dcad2727b69a -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_0.cs b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_0.cs deleted file mode 100644 index 6f859e384c3..00000000000 --- a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_0.cs +++ /dev/null @@ -1,178 +0,0 @@ -// DO NOT EDIT: GENERATED BY QuaternionPackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.QuaternionPackAttributeTests._8_0 -{ - public class BitPackBehaviour : NetworkBehaviour - { - [QuaternionPack(8)] - [SyncVar] public Quaternion myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([QuaternionPack(8)] Quaternion myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [QuaternionPack(8)] - public Quaternion myValue; - } - - [Serializable] - public struct BitPackStruct - { - [QuaternionPack(8)] - public Quaternion myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Quaternion value = new Quaternion(0f, 0.7071068f, 0f, 0.7071068f); - private const float within = 0.0054f; - - private static void AssertValue(Quaternion actual) - { - Vector3 inVec = value * Vector3.forward; - Vector3 outVec = actual * Vector3.forward; - - // allow for extra within when rotating vector - Assert.AreEqual(inVec.x, outVec.x, within * 2, $"vx off by {Mathf.Abs(inVec.x - outVec.x)}"); - Assert.AreEqual(inVec.y, outVec.y, within * 2, $"vy off by {Mathf.Abs(inVec.y - outVec.y)}"); - Assert.AreEqual(inVec.z, outVec.z, within * 2, $"vz off by {Mathf.Abs(inVec.z - outVec.z)}"); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(26)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(26)); - - AssertValue(clientComponent.myValue); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (26 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"26 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((26 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"26 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(26)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(26)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_0.cs.meta b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_0.cs.meta deleted file mode 100644 index dea1117b642..00000000000 --- a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_0.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 9ee00da2473d29a4699af571979e004a -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_45.cs b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_45.cs deleted file mode 100644 index 941c3c27d5c..00000000000 --- a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_45.cs +++ /dev/null @@ -1,178 +0,0 @@ -// DO NOT EDIT: GENERATED BY QuaternionPackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.QuaternionPackAttributeTests._8_45 -{ - public class BitPackBehaviour : NetworkBehaviour - { - [QuaternionPack(8)] - [SyncVar] public Quaternion myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([QuaternionPack(8)] Quaternion myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [QuaternionPack(8)] - public Quaternion myValue; - } - - [Serializable] - public struct BitPackStruct - { - [QuaternionPack(8)] - public Quaternion myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Quaternion value = new Quaternion(0.2705981f, 0.6532815f, -0.2705981f, 0.6532815f); - private const float within = 0.0054f; - - private static void AssertValue(Quaternion actual) - { - Vector3 inVec = value * Vector3.forward; - Vector3 outVec = actual * Vector3.forward; - - // allow for extra within when rotating vector - Assert.AreEqual(inVec.x, outVec.x, within * 2, $"vx off by {Mathf.Abs(inVec.x - outVec.x)}"); - Assert.AreEqual(inVec.y, outVec.y, within * 2, $"vy off by {Mathf.Abs(inVec.y - outVec.y)}"); - Assert.AreEqual(inVec.z, outVec.z, within * 2, $"vz off by {Mathf.Abs(inVec.z - outVec.z)}"); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(26)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(26)); - - AssertValue(clientComponent.myValue); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (26 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"26 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((26 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"26 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(26)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(26)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_45.cs.meta b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_45.cs.meta deleted file mode 100644 index 9e86c248805..00000000000 --- a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_45.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: ff61adfaa015039468c52bd397fbb3b6 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_0.cs b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_0.cs deleted file mode 100644 index 70af3cdeec2..00000000000 --- a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_0.cs +++ /dev/null @@ -1,178 +0,0 @@ -// DO NOT EDIT: GENERATED BY QuaternionPackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.QuaternionPackAttributeTests._9_0 -{ - public class BitPackBehaviour : NetworkBehaviour - { - [QuaternionPack(9)] - [SyncVar] public Quaternion myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([QuaternionPack(9)] Quaternion myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [QuaternionPack(9)] - public Quaternion myValue; - } - - [Serializable] - public struct BitPackStruct - { - [QuaternionPack(9)] - public Quaternion myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Quaternion value = new Quaternion(0f, 0.7071068f, 0f, 0.7071068f); - private const float within = 0.0027f; - - private static void AssertValue(Quaternion actual) - { - Vector3 inVec = value * Vector3.forward; - Vector3 outVec = actual * Vector3.forward; - - // allow for extra within when rotating vector - Assert.AreEqual(inVec.x, outVec.x, within * 2, $"vx off by {Mathf.Abs(inVec.x - outVec.x)}"); - Assert.AreEqual(inVec.y, outVec.y, within * 2, $"vy off by {Mathf.Abs(inVec.y - outVec.y)}"); - Assert.AreEqual(inVec.z, outVec.z, within * 2, $"vz off by {Mathf.Abs(inVec.z - outVec.z)}"); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(29)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(29)); - - AssertValue(clientComponent.myValue); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (29 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"29 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((29 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"29 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(29)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(29)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_0.cs.meta b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_0.cs.meta deleted file mode 100644 index 796d984c61f..00000000000 --- a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_0.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: e96f6231777d2634783d2c5b3f01d2ae -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_45.cs b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_45.cs deleted file mode 100644 index d2939b16b4f..00000000000 --- a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_45.cs +++ /dev/null @@ -1,178 +0,0 @@ -// DO NOT EDIT: GENERATED BY QuaternionPackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.QuaternionPackAttributeTests._9_45 -{ - public class BitPackBehaviour : NetworkBehaviour - { - [QuaternionPack(9)] - [SyncVar] public Quaternion myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([QuaternionPack(9)] Quaternion myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [QuaternionPack(9)] - public Quaternion myValue; - } - - [Serializable] - public struct BitPackStruct - { - [QuaternionPack(9)] - public Quaternion myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Quaternion value = new Quaternion(0.2705981f, 0.6532815f, -0.2705981f, 0.6532815f); - private const float within = 0.0027f; - - private static void AssertValue(Quaternion actual) - { - Vector3 inVec = value * Vector3.forward; - Vector3 outVec = actual * Vector3.forward; - - // allow for extra within when rotating vector - Assert.AreEqual(inVec.x, outVec.x, within * 2, $"vx off by {Mathf.Abs(inVec.x - outVec.x)}"); - Assert.AreEqual(inVec.y, outVec.y, within * 2, $"vy off by {Mathf.Abs(inVec.y - outVec.y)}"); - Assert.AreEqual(inVec.z, outVec.z, within * 2, $"vz off by {Mathf.Abs(inVec.z - outVec.z)}"); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(29)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(29)); - - AssertValue(clientComponent.myValue); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (29 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"29 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((29 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"29 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(29)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(29)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_45.cs.meta b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_45.cs.meta deleted file mode 100644 index be847736caa..00000000000 --- a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_45.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: eb3e6e061e904fc45abc3bfc793042db -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntBlocksTests.meta b/Assets/Tests/Generated/VarIntBlocksTests.meta deleted file mode 100644 index 3cc414c7628..00000000000 --- a/Assets/Tests/Generated/VarIntBlocksTests.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 54cdd4dda64543b41b134919dfe5f4ca -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnumByte_4.cs b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnumByte_4.cs deleted file mode 100644 index c7c6d6cc814..00000000000 --- a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnumByte_4.cs +++ /dev/null @@ -1,203 +0,0 @@ -// DO NOT EDIT: GENERATED BY VarIntBlocksTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.VarIntBlocksTests.MyEnumByte_4 -{ - [System.Flags, System.Serializable] - public enum MyEnumByte : byte - { - None = 0, - HasHealth = 1, - HasArmor = 2, - HasGun = 4, - HasAmmo = 8, - HasLeftHand = 16, - HasRightHand = 32, - HasHead = 64, - } - public class BitPackBehaviour : NetworkBehaviour - { - [VarIntBlocks(4)] - [SyncVar] public MyEnumByte myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([VarIntBlocks(4)] MyEnumByte myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [VarIntBlocks(4)] - public MyEnumByte myValue; - } - - [Serializable] - public struct BitPackStruct - { - [VarIntBlocks(4)] - public MyEnumByte myValue; - } - - public class BitPackTest : ClientServerSetup - { - public struct TestCase - { - public MyEnumByte value; - public int expectedBits; - public override string ToString() => value.ToString(); - } - - private static TestCase[] cases = new TestCase[] - { - new TestCase { value = (MyEnumByte)0, expectedBits = 5 }, - new TestCase { value = (MyEnumByte)4, expectedBits = 5 }, - new TestCase { value = (MyEnumByte)16, expectedBits = 10 }, - new TestCase { value = (MyEnumByte)64, expectedBits = 10 } - }; - - [Test] - public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - MyEnumByte value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - MyEnumByte value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (expectedBitCount + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - MyEnumByte value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - MyEnumByte value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnumByte_4.cs.meta b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnumByte_4.cs.meta deleted file mode 100644 index 7370d76690e..00000000000 --- a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnumByte_4.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: dd58ec5b813985049bc40fec3e8634ba -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnum_4.cs b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnum_4.cs deleted file mode 100644 index ab9954b0aa9..00000000000 --- a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnum_4.cs +++ /dev/null @@ -1,203 +0,0 @@ -// DO NOT EDIT: GENERATED BY VarIntBlocksTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.VarIntBlocksTests.MyEnum_4 -{ - [System.Flags, System.Serializable] - public enum MyEnum - { - None = 0, - HasHealth = 1, - HasArmor = 2, - HasGun = 4, - HasAmmo = 8, - HasLeftHand = 16, - HasRightHand = 32, - HasHead = 64, - } - public class BitPackBehaviour : NetworkBehaviour - { - [VarIntBlocks(4)] - [SyncVar] public MyEnum myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([VarIntBlocks(4)] MyEnum myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [VarIntBlocks(4)] - public MyEnum myValue; - } - - [Serializable] - public struct BitPackStruct - { - [VarIntBlocks(4)] - public MyEnum myValue; - } - - public class BitPackTest : ClientServerSetup - { - public struct TestCase - { - public MyEnum value; - public int expectedBits; - public override string ToString() => value.ToString(); - } - - private static TestCase[] cases = new TestCase[] - { - new TestCase { value = (MyEnum)0, expectedBits = 5 }, - new TestCase { value = (MyEnum)4, expectedBits = 5 }, - new TestCase { value = (MyEnum)16, expectedBits = 10 }, - new TestCase { value = (MyEnum)64, expectedBits = 10 } - }; - - [Test] - public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - MyEnum value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - MyEnum value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (expectedBitCount + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - MyEnum value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - MyEnum value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnum_4.cs.meta b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnum_4.cs.meta deleted file mode 100644 index 0a48a27de04..00000000000 --- a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnum_4.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 8564970176bb5a343aa8762c1b5daa19 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_6.cs b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_6.cs deleted file mode 100644 index fde731d573c..00000000000 --- a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_6.cs +++ /dev/null @@ -1,192 +0,0 @@ -// DO NOT EDIT: GENERATED BY VarIntBlocksTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.VarIntBlocksTests.int_6 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [VarIntBlocks(6)] - [SyncVar] public int myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([VarIntBlocks(6)] int myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [VarIntBlocks(6)] - public int myValue; - } - - [Serializable] - public struct BitPackStruct - { - [VarIntBlocks(6)] - public int myValue; - } - - public class BitPackTest : ClientServerSetup - { - public struct TestCase - { - public int value; - public int expectedBits; - public override string ToString() => value.ToString(); - } - - private static TestCase[] cases = new TestCase[] - { - new TestCase { value = 10, expectedBits = 7 }, - new TestCase { value = 100, expectedBits = 14 }, - new TestCase { value = 1000, expectedBits = 14 }, - new TestCase { value = 10000, expectedBits = 21 } - }; - - [Test] - public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - int value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - int value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (expectedBitCount + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - int value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - int value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_6.cs.meta b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_6.cs.meta deleted file mode 100644 index ef4fb1dd5dc..00000000000 --- a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_6.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: c633b38fb69d241479d23a9938cbde2a -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_7.cs b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_7.cs deleted file mode 100644 index ec403fc816a..00000000000 --- a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_7.cs +++ /dev/null @@ -1,191 +0,0 @@ -// DO NOT EDIT: GENERATED BY VarIntBlocksTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.VarIntBlocksTests.int_7 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [VarIntBlocks(7)] - [SyncVar] public int myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([VarIntBlocks(7)] int myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [VarIntBlocks(7)] - public int myValue; - } - - [Serializable] - public struct BitPackStruct - { - [VarIntBlocks(7)] - public int myValue; - } - - public class BitPackTest : ClientServerSetup - { - public struct TestCase - { - public int value; - public int expectedBits; - public override string ToString() => value.ToString(); - } - - private static TestCase[] cases = new TestCase[] - { - new TestCase { value = 10, expectedBits = 8 }, - new TestCase { value = 100, expectedBits = 8 }, - new TestCase { value = 1000, expectedBits = 16 } - }; - - [Test] - public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - int value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - int value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (expectedBitCount + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - int value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - int value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_7.cs.meta b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_7.cs.meta deleted file mode 100644 index 3e1ae7224f3..00000000000 --- a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_7.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 0ed4343ce13b5974fb968b088630ef72 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_long_8.cs b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_long_8.cs deleted file mode 100644 index c21fee6d6ba..00000000000 --- a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_long_8.cs +++ /dev/null @@ -1,192 +0,0 @@ -// DO NOT EDIT: GENERATED BY VarIntBlocksTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.VarIntBlocksTests.long_8 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [VarIntBlocks(8)] - [SyncVar] public long myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([VarIntBlocks(8)] long myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [VarIntBlocks(8)] - public long myValue; - } - - [Serializable] - public struct BitPackStruct - { - [VarIntBlocks(8)] - public long myValue; - } - - public class BitPackTest : ClientServerSetup - { - public struct TestCase - { - public long value; - public int expectedBits; - public override string ToString() => value.ToString(); - } - - private static TestCase[] cases = new TestCase[] - { - new TestCase { value = 10L, expectedBits = 9 }, - new TestCase { value = 100L, expectedBits = 9 }, - new TestCase { value = 1000L, expectedBits = 18 }, - new TestCase { value = 10000L, expectedBits = 18 } - }; - - [Test] - public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - long value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - long value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (expectedBitCount + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - long value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - long value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_long_8.cs.meta b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_long_8.cs.meta deleted file mode 100644 index 95f0c3b0423..00000000000 --- a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_long_8.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 512c8abf0775f604d96957c9a852b386 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_short_6.cs b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_short_6.cs deleted file mode 100644 index 1333647b118..00000000000 --- a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_short_6.cs +++ /dev/null @@ -1,192 +0,0 @@ -// DO NOT EDIT: GENERATED BY VarIntBlocksTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.VarIntBlocksTests.short_6 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [VarIntBlocks(6)] - [SyncVar] public short myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([VarIntBlocks(6)] short myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [VarIntBlocks(6)] - public short myValue; - } - - [Serializable] - public struct BitPackStruct - { - [VarIntBlocks(6)] - public short myValue; - } - - public class BitPackTest : ClientServerSetup - { - public struct TestCase - { - public short value; - public int expectedBits; - public override string ToString() => value.ToString(); - } - - private static TestCase[] cases = new TestCase[] - { - new TestCase { value = (short)10, expectedBits = 7 }, - new TestCase { value = (short)100, expectedBits = 14 }, - new TestCase { value = (short)1000, expectedBits = 14 }, - new TestCase { value = (short)10000, expectedBits = 21 } - }; - - [Test] - public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - short value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - short value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (expectedBitCount + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - short value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - short value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_short_6.cs.meta b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_short_6.cs.meta deleted file mode 100644 index 8abbae4ab18..00000000000 --- a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_short_6.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 6de4b164f86fb5b4f80251d7e2ba68f2 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_6.cs b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_6.cs deleted file mode 100644 index b5745112bbd..00000000000 --- a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_6.cs +++ /dev/null @@ -1,192 +0,0 @@ -// DO NOT EDIT: GENERATED BY VarIntBlocksTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.VarIntBlocksTests.uint_6 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [VarIntBlocks(6)] - [SyncVar] public uint myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([VarIntBlocks(6)] uint myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [VarIntBlocks(6)] - public uint myValue; - } - - [Serializable] - public struct BitPackStruct - { - [VarIntBlocks(6)] - public uint myValue; - } - - public class BitPackTest : ClientServerSetup - { - public struct TestCase - { - public uint value; - public int expectedBits; - public override string ToString() => value.ToString(); - } - - private static TestCase[] cases = new TestCase[] - { - new TestCase { value = 170U, expectedBits = 14 }, - new TestCase { value = 500U, expectedBits = 14 }, - new TestCase { value = 15000U, expectedBits = 21 }, - new TestCase { value = 50000U, expectedBits = 21 } - }; - - [Test] - public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - uint value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - uint value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (expectedBitCount + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - uint value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - uint value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_6.cs.meta b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_6.cs.meta deleted file mode 100644 index 6ce35e9fdd7..00000000000 --- a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_6.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: faffd93deb00a404ab43df3a310408af -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_7.cs b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_7.cs deleted file mode 100644 index 29a1c9fce72..00000000000 --- a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_7.cs +++ /dev/null @@ -1,192 +0,0 @@ -// DO NOT EDIT: GENERATED BY VarIntBlocksTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.VarIntBlocksTests.uint_7 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [VarIntBlocks(7)] - [SyncVar] public uint myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([VarIntBlocks(7)] uint myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [VarIntBlocks(7)] - public uint myValue; - } - - [Serializable] - public struct BitPackStruct - { - [VarIntBlocks(7)] - public uint myValue; - } - - public class BitPackTest : ClientServerSetup - { - public struct TestCase - { - public uint value; - public int expectedBits; - public override string ToString() => value.ToString(); - } - - private static TestCase[] cases = new TestCase[] - { - new TestCase { value = 10U, expectedBits = 8 }, - new TestCase { value = 100U, expectedBits = 8 }, - new TestCase { value = 1000U, expectedBits = 16 }, - new TestCase { value = 10000U, expectedBits = 16 } - }; - - [Test] - public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - uint value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - uint value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (expectedBitCount + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - uint value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - uint value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_7.cs.meta b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_7.cs.meta deleted file mode 100644 index 2a12a3f452e..00000000000 --- a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_7.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 0bd2a4ccf2c2b304a9e8ff665cd01f0f -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_8.cs b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_8.cs deleted file mode 100644 index 8dc4f889053..00000000000 --- a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_8.cs +++ /dev/null @@ -1,193 +0,0 @@ -// DO NOT EDIT: GENERATED BY VarIntBlocksTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.VarIntBlocksTests.uint_8 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [VarIntBlocks(8)] - [SyncVar] public uint myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([VarIntBlocks(8)] uint myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [VarIntBlocks(8)] - public uint myValue; - } - - [Serializable] - public struct BitPackStruct - { - [VarIntBlocks(8)] - public uint myValue; - } - - public class BitPackTest : ClientServerSetup - { - public struct TestCase - { - public uint value; - public int expectedBits; - public override string ToString() => value.ToString(); - } - - private static TestCase[] cases = new TestCase[] - { - new TestCase { value = 170U, expectedBits = 9 }, - new TestCase { value = 500U, expectedBits = 18 }, - new TestCase { value = 15000U, expectedBits = 18 }, - new TestCase { value = 50000U, expectedBits = 18 }, - new TestCase { value = 400000U, expectedBits = 27 } - }; - - [Test] - public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - uint value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - uint value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (expectedBitCount + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - uint value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - uint value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_8.cs.meta b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_8.cs.meta deleted file mode 100644 index 3e75596f1c2..00000000000 --- a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_8.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 7f9207744d86d874a9eb7f04770fe84b -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ulong_9.cs b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ulong_9.cs deleted file mode 100644 index 17d3b675a77..00000000000 --- a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ulong_9.cs +++ /dev/null @@ -1,192 +0,0 @@ -// DO NOT EDIT: GENERATED BY VarIntBlocksTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.VarIntBlocksTests.ulong_9 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [VarIntBlocks(9)] - [SyncVar] public ulong myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([VarIntBlocks(9)] ulong myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [VarIntBlocks(9)] - public ulong myValue; - } - - [Serializable] - public struct BitPackStruct - { - [VarIntBlocks(9)] - public ulong myValue; - } - - public class BitPackTest : ClientServerSetup - { - public struct TestCase - { - public ulong value; - public int expectedBits; - public override string ToString() => value.ToString(); - } - - private static TestCase[] cases = new TestCase[] - { - new TestCase { value = 10UL, expectedBits = 10 }, - new TestCase { value = 100UL, expectedBits = 10 }, - new TestCase { value = 1000UL, expectedBits = 20 }, - new TestCase { value = 10000UL, expectedBits = 20 } - }; - - [Test] - public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - ulong value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - ulong value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (expectedBitCount + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - ulong value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - ulong value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ulong_9.cs.meta b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ulong_9.cs.meta deleted file mode 100644 index 69489e41e43..00000000000 --- a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ulong_9.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: f530467d1062c8e4e979086df243dab5 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ushort_7.cs b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ushort_7.cs deleted file mode 100644 index 0878a8bce53..00000000000 --- a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ushort_7.cs +++ /dev/null @@ -1,192 +0,0 @@ -// DO NOT EDIT: GENERATED BY VarIntBlocksTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.VarIntBlocksTests.ushort_7 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [VarIntBlocks(7)] - [SyncVar] public ushort myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([VarIntBlocks(7)] ushort myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [VarIntBlocks(7)] - public ushort myValue; - } - - [Serializable] - public struct BitPackStruct - { - [VarIntBlocks(7)] - public ushort myValue; - } - - public class BitPackTest : ClientServerSetup - { - public struct TestCase - { - public ushort value; - public int expectedBits; - public override string ToString() => value.ToString(); - } - - private static TestCase[] cases = new TestCase[] - { - new TestCase { value = (ushort)10, expectedBits = 8 }, - new TestCase { value = (ushort)100, expectedBits = 8 }, - new TestCase { value = (ushort)1000, expectedBits = 16 }, - new TestCase { value = (ushort)10000, expectedBits = 16 } - }; - - [Test] - public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - ushort value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - ushort value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (expectedBitCount + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - ushort value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - ushort value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ushort_7.cs.meta b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ushort_7.cs.meta deleted file mode 100644 index 2f7e7ea744d..00000000000 --- a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ushort_7.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: b533f40ccf5335246ba9f2ea00e9a25b -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntTests.meta b/Assets/Tests/Generated/VarIntTests.meta deleted file mode 100644 index 01a378d165f..00000000000 --- a/Assets/Tests/Generated/VarIntTests.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: b563527bc175b924d9137fbc7b401a4a -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnumByte_4_64.cs b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnumByte_4_64.cs deleted file mode 100644 index 1e5a265082a..00000000000 --- a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnumByte_4_64.cs +++ /dev/null @@ -1,203 +0,0 @@ -// DO NOT EDIT: GENERATED BY VarIntTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.VarIntTests.MyEnumByte_4_64 -{ - [System.Flags, System.Serializable] - public enum MyEnumByte : byte - { - None = 0, - HasHealth = 1, - HasArmor = 2, - HasGun = 4, - HasAmmo = 8, - HasLeftHand = 16, - HasRightHand = 32, - HasHead = 64, - } - public class BitPackBehaviour : NetworkBehaviour - { - [VarInt(4, 64)] - [SyncVar] public MyEnumByte myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([VarInt(4, 64)] MyEnumByte myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [VarInt(4, 64)] - public MyEnumByte myValue; - } - - [Serializable] - public struct BitPackStruct - { - [VarInt(4, 64)] - public MyEnumByte myValue; - } - - public class BitPackTest : ClientServerSetup - { - public struct TestCase - { - public MyEnumByte value; - public int expectedBits; - public override string ToString() => value.ToString(); - } - - private static TestCase[] cases = new TestCase[] - { - new TestCase { value = (MyEnumByte)0, expectedBits = 4 }, - new TestCase { value = (MyEnumByte)4, expectedBits = 4 }, - new TestCase { value = (MyEnumByte)16, expectedBits = 9 }, - new TestCase { value = (MyEnumByte)64, expectedBits = 9 } - }; - - [Test] - public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - MyEnumByte value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - MyEnumByte value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (expectedBitCount + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - MyEnumByte value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - MyEnumByte value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnumByte_4_64.cs.meta b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnumByte_4_64.cs.meta deleted file mode 100644 index 9d7e52352e4..00000000000 --- a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnumByte_4_64.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 058da6460bf60964e80bdfaa3e0483e3 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnum_4_64.cs b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnum_4_64.cs deleted file mode 100644 index 76f46d9dd49..00000000000 --- a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnum_4_64.cs +++ /dev/null @@ -1,203 +0,0 @@ -// DO NOT EDIT: GENERATED BY VarIntTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.VarIntTests.MyEnum_4_64 -{ - [System.Flags, System.Serializable] - public enum MyEnum - { - None = 0, - HasHealth = 1, - HasArmor = 2, - HasGun = 4, - HasAmmo = 8, - HasLeftHand = 16, - HasRightHand = 32, - HasHead = 64, - } - public class BitPackBehaviour : NetworkBehaviour - { - [VarInt(4, 64)] - [SyncVar] public MyEnum myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([VarInt(4, 64)] MyEnum myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [VarInt(4, 64)] - public MyEnum myValue; - } - - [Serializable] - public struct BitPackStruct - { - [VarInt(4, 64)] - public MyEnum myValue; - } - - public class BitPackTest : ClientServerSetup - { - public struct TestCase - { - public MyEnum value; - public int expectedBits; - public override string ToString() => value.ToString(); - } - - private static TestCase[] cases = new TestCase[] - { - new TestCase { value = (MyEnum)0, expectedBits = 4 }, - new TestCase { value = (MyEnum)4, expectedBits = 4 }, - new TestCase { value = (MyEnum)16, expectedBits = 9 }, - new TestCase { value = (MyEnum)64, expectedBits = 9 } - }; - - [Test] - public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - MyEnum value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - MyEnum value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (expectedBitCount + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - MyEnum value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - MyEnum value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnum_4_64.cs.meta b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnum_4_64.cs.meta deleted file mode 100644 index 631a6f0275a..00000000000 --- a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnum_4_64.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: ae539f5208a0fdb419186c45080bf2a9 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_1000.cs b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_1000.cs deleted file mode 100644 index 1e46a920e8d..00000000000 --- a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_1000.cs +++ /dev/null @@ -1,192 +0,0 @@ -// DO NOT EDIT: GENERATED BY VarIntTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.VarIntTests.int_100_1000 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [VarInt(100, 1000, 10000)] - [SyncVar] public int myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([VarInt(100, 1000, 10000)] int myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [VarInt(100, 1000, 10000)] - public int myValue; - } - - [Serializable] - public struct BitPackStruct - { - [VarInt(100, 1000, 10000)] - public int myValue; - } - - public class BitPackTest : ClientServerSetup - { - public struct TestCase - { - public int value; - public int expectedBits; - public override string ToString() => value.ToString(); - } - - private static TestCase[] cases = new TestCase[] - { - new TestCase { value = 10, expectedBits = 8 }, - new TestCase { value = 100, expectedBits = 8 }, - new TestCase { value = 1000, expectedBits = 12 }, - new TestCase { value = 10000, expectedBits = 16 } - }; - - [Test] - public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - int value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - int value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (expectedBitCount + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - int value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - int value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_1000.cs.meta b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_1000.cs.meta deleted file mode 100644 index 09f3e4de21b..00000000000 --- a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_1000.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: f3ea125124510384f8cec89825e54ebc -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_10000.cs b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_10000.cs deleted file mode 100644 index e70eb1a517d..00000000000 --- a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_10000.cs +++ /dev/null @@ -1,191 +0,0 @@ -// DO NOT EDIT: GENERATED BY VarIntTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.VarIntTests.int_100_10000 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [VarInt(100, 10000)] - [SyncVar] public int myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([VarInt(100, 10000)] int myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [VarInt(100, 10000)] - public int myValue; - } - - [Serializable] - public struct BitPackStruct - { - [VarInt(100, 10000)] - public int myValue; - } - - public class BitPackTest : ClientServerSetup - { - public struct TestCase - { - public int value; - public int expectedBits; - public override string ToString() => value.ToString(); - } - - private static TestCase[] cases = new TestCase[] - { - new TestCase { value = 10, expectedBits = 8 }, - new TestCase { value = 100, expectedBits = 8 }, - new TestCase { value = 1000, expectedBits = 16 } - }; - - [Test] - public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - int value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - int value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (expectedBitCount + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - int value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - int value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_10000.cs.meta b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_10000.cs.meta deleted file mode 100644 index de2e4028be1..00000000000 --- a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_10000.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: bcf75fcd76c9e4046bfe4e219d3db071 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_long_100_1000.cs b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_long_100_1000.cs deleted file mode 100644 index 9482409d9c2..00000000000 --- a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_long_100_1000.cs +++ /dev/null @@ -1,192 +0,0 @@ -// DO NOT EDIT: GENERATED BY VarIntTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.VarIntTests.long_100_1000 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [VarInt(100, 1000, 10000)] - [SyncVar] public long myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([VarInt(100, 1000, 10000)] long myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [VarInt(100, 1000, 10000)] - public long myValue; - } - - [Serializable] - public struct BitPackStruct - { - [VarInt(100, 1000, 10000)] - public long myValue; - } - - public class BitPackTest : ClientServerSetup - { - public struct TestCase - { - public long value; - public int expectedBits; - public override string ToString() => value.ToString(); - } - - private static TestCase[] cases = new TestCase[] - { - new TestCase { value = 10L, expectedBits = 8 }, - new TestCase { value = 100L, expectedBits = 8 }, - new TestCase { value = 1000L, expectedBits = 12 }, - new TestCase { value = 10000L, expectedBits = 16 } - }; - - [Test] - public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - long value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - long value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (expectedBitCount + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - long value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - long value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_long_100_1000.cs.meta b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_long_100_1000.cs.meta deleted file mode 100644 index 61e222c1673..00000000000 --- a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_long_100_1000.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 05bd3b7b99eeb7b41a045b4585b0c494 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_short_100_1000.cs b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_short_100_1000.cs deleted file mode 100644 index 749b302ecb9..00000000000 --- a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_short_100_1000.cs +++ /dev/null @@ -1,192 +0,0 @@ -// DO NOT EDIT: GENERATED BY VarIntTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.VarIntTests.short_100_1000 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [VarInt(100, 1000, 10000)] - [SyncVar] public short myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([VarInt(100, 1000, 10000)] short myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [VarInt(100, 1000, 10000)] - public short myValue; - } - - [Serializable] - public struct BitPackStruct - { - [VarInt(100, 1000, 10000)] - public short myValue; - } - - public class BitPackTest : ClientServerSetup - { - public struct TestCase - { - public short value; - public int expectedBits; - public override string ToString() => value.ToString(); - } - - private static TestCase[] cases = new TestCase[] - { - new TestCase { value = (short)10, expectedBits = 8 }, - new TestCase { value = (short)100, expectedBits = 8 }, - new TestCase { value = (short)1000, expectedBits = 12 }, - new TestCase { value = (short)10000, expectedBits = 16 } - }; - - [Test] - public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - short value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - short value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (expectedBitCount + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - short value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - short value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_short_100_1000.cs.meta b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_short_100_1000.cs.meta deleted file mode 100644 index 1b94954749b..00000000000 --- a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_short_100_1000.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 5b8e7ddcab5766b42837e2e90993cc86 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_100_1000.cs b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_100_1000.cs deleted file mode 100644 index 7204fb37f18..00000000000 --- a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_100_1000.cs +++ /dev/null @@ -1,192 +0,0 @@ -// DO NOT EDIT: GENERATED BY VarIntTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.VarIntTests.uint_100_1000 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [VarInt(100, 1000, 10000)] - [SyncVar] public uint myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([VarInt(100, 1000, 10000)] uint myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [VarInt(100, 1000, 10000)] - public uint myValue; - } - - [Serializable] - public struct BitPackStruct - { - [VarInt(100, 1000, 10000)] - public uint myValue; - } - - public class BitPackTest : ClientServerSetup - { - public struct TestCase - { - public uint value; - public int expectedBits; - public override string ToString() => value.ToString(); - } - - private static TestCase[] cases = new TestCase[] - { - new TestCase { value = 10U, expectedBits = 8 }, - new TestCase { value = 100U, expectedBits = 8 }, - new TestCase { value = 1000U, expectedBits = 12 }, - new TestCase { value = 10000U, expectedBits = 16 } - }; - - [Test] - public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - uint value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - uint value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (expectedBitCount + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - uint value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - uint value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_100_1000.cs.meta b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_100_1000.cs.meta deleted file mode 100644 index dc77a568c58..00000000000 --- a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_100_1000.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 2e31b41d99f9064419329854894d1f3f -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_255_64000.cs b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_255_64000.cs deleted file mode 100644 index 154f8f4d1c0..00000000000 --- a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_255_64000.cs +++ /dev/null @@ -1,192 +0,0 @@ -// DO NOT EDIT: GENERATED BY VarIntTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.VarIntTests.uint_255_64000 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [VarInt(255, 64000)] - [SyncVar] public uint myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([VarInt(255, 64000)] uint myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [VarInt(255, 64000)] - public uint myValue; - } - - [Serializable] - public struct BitPackStruct - { - [VarInt(255, 64000)] - public uint myValue; - } - - public class BitPackTest : ClientServerSetup - { - public struct TestCase - { - public uint value; - public int expectedBits; - public override string ToString() => value.ToString(); - } - - private static TestCase[] cases = new TestCase[] - { - new TestCase { value = 170U, expectedBits = 9 }, - new TestCase { value = 500U, expectedBits = 18 }, - new TestCase { value = 15000U, expectedBits = 18 }, - new TestCase { value = 50000U, expectedBits = 18 } - }; - - [Test] - public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - uint value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - uint value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (expectedBitCount + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - uint value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - uint value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_255_64000.cs.meta b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_255_64000.cs.meta deleted file mode 100644 index 816caf767be..00000000000 --- a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_255_64000.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 5c62e5784039a1d4283f53d29814edac -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_500_32000.cs b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_500_32000.cs deleted file mode 100644 index 7ce10a12d6c..00000000000 --- a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_500_32000.cs +++ /dev/null @@ -1,193 +0,0 @@ -// DO NOT EDIT: GENERATED BY VarIntTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.VarIntTests.uint_500_32000 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [VarInt(500, 32000, 2000000)] - [SyncVar] public uint myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([VarInt(500, 32000, 2000000)] uint myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [VarInt(500, 32000, 2000000)] - public uint myValue; - } - - [Serializable] - public struct BitPackStruct - { - [VarInt(500, 32000, 2000000)] - public uint myValue; - } - - public class BitPackTest : ClientServerSetup - { - public struct TestCase - { - public uint value; - public int expectedBits; - public override string ToString() => value.ToString(); - } - - private static TestCase[] cases = new TestCase[] - { - new TestCase { value = 170U, expectedBits = 10 }, - new TestCase { value = 500U, expectedBits = 10 }, - new TestCase { value = 15000U, expectedBits = 17 }, - new TestCase { value = 50000U, expectedBits = 23 }, - new TestCase { value = 400000U, expectedBits = 23 } - }; - - [Test] - public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - uint value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - uint value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (expectedBitCount + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - uint value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - uint value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_500_32000.cs.meta b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_500_32000.cs.meta deleted file mode 100644 index 1bc64c1763f..00000000000 --- a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_500_32000.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 9afb405e75120264aad8abce8467336c -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ulong_100_1000.cs b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ulong_100_1000.cs deleted file mode 100644 index c80b486408f..00000000000 --- a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ulong_100_1000.cs +++ /dev/null @@ -1,192 +0,0 @@ -// DO NOT EDIT: GENERATED BY VarIntTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.VarIntTests.ulong_100_1000 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [VarInt(100, 1000, 10000)] - [SyncVar] public ulong myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([VarInt(100, 1000, 10000)] ulong myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [VarInt(100, 1000, 10000)] - public ulong myValue; - } - - [Serializable] - public struct BitPackStruct - { - [VarInt(100, 1000, 10000)] - public ulong myValue; - } - - public class BitPackTest : ClientServerSetup - { - public struct TestCase - { - public ulong value; - public int expectedBits; - public override string ToString() => value.ToString(); - } - - private static TestCase[] cases = new TestCase[] - { - new TestCase { value = 10UL, expectedBits = 8 }, - new TestCase { value = 100UL, expectedBits = 8 }, - new TestCase { value = 1000UL, expectedBits = 12 }, - new TestCase { value = 10000UL, expectedBits = 16 } - }; - - [Test] - public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - ulong value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - ulong value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (expectedBitCount + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - ulong value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - ulong value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ulong_100_1000.cs.meta b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ulong_100_1000.cs.meta deleted file mode 100644 index 9e693f73501..00000000000 --- a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ulong_100_1000.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: d86a0360c302b1743b3d81fd839cee80 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ushort_100_1000.cs b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ushort_100_1000.cs deleted file mode 100644 index 53be5ae1615..00000000000 --- a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ushort_100_1000.cs +++ /dev/null @@ -1,192 +0,0 @@ -// DO NOT EDIT: GENERATED BY VarIntTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.VarIntTests.ushort_100_1000 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [VarInt(100, 1000, 10000)] - [SyncVar] public ushort myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([VarInt(100, 1000, 10000)] ushort myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [VarInt(100, 1000, 10000)] - public ushort myValue; - } - - [Serializable] - public struct BitPackStruct - { - [VarInt(100, 1000, 10000)] - public ushort myValue; - } - - public class BitPackTest : ClientServerSetup - { - public struct TestCase - { - public ushort value; - public int expectedBits; - public override string ToString() => value.ToString(); - } - - private static TestCase[] cases = new TestCase[] - { - new TestCase { value = (ushort)10, expectedBits = 8 }, - new TestCase { value = (ushort)100, expectedBits = 8 }, - new TestCase { value = (ushort)1000, expectedBits = 12 }, - new TestCase { value = (ushort)10000, expectedBits = 16 } - }; - - [Test] - public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - ushort value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - ushort value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (expectedBitCount + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - ushort value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) - { - ushort value = TestCase.value; - int expectedBitCount = TestCase.expectedBits; - - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ushort_100_1000.cs.meta b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ushort_100_1000.cs.meta deleted file mode 100644 index e33e9c038e2..00000000000 --- a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ushort_100_1000.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 80ae2ac144aa18444a2b09c45395c714 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector2PackTests.meta b/Assets/Tests/Generated/Vector2PackTests.meta deleted file mode 100644 index d0b299f5bdf..00000000000 --- a/Assets/Tests/Generated/Vector2PackTests.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 799eefc3ace83a8419c274dc0e7e875e -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27b2.cs b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27b2.cs deleted file mode 100644 index 91e238a3b3e..00000000000 --- a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27b2.cs +++ /dev/null @@ -1,173 +0,0 @@ -// DO NOT EDIT: GENERATED BY Vector2PackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.Vector2PackAttributeTests._1000_27b2 -{ - public class BitPackBehaviour : NetworkBehaviour - { - [Vector2Pack(1000f, 200f, 15, 12)] - [SyncVar] public Vector2 myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction( [Vector2Pack(1000f, 200f, 15, 12)] Vector2 myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [Vector2Pack(1000f, 200f, 15, 12)] - public Vector2 myValue; - } - - [Serializable] - public struct BitPackStruct - { - [Vector2Pack(1000f, 200f, 15, 12)] - public Vector2 myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Vector2 value = new Vector2(10.3f, 0.2f); - private const float within = 0.2f; - - private static void AssertValue(Vector2 actual) - { - Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); - Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(27)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(27)); - - AssertValue(clientComponent.myValue); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (27 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"27 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((27 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"27 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(27)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(27)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27b2.cs.meta b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27b2.cs.meta deleted file mode 100644 index 28b21d3b949..00000000000 --- a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27b2.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: dafa52920fbbcb74a948431f18d74051 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f.cs b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f.cs deleted file mode 100644 index 1b6bce3ccae..00000000000 --- a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f.cs +++ /dev/null @@ -1,173 +0,0 @@ -// DO NOT EDIT: GENERATED BY Vector2PackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.Vector2PackAttributeTests._1000_27f -{ - public class BitPackBehaviour : NetworkBehaviour - { - [Vector2Pack(1000f, 200f, 0.1f)] - [SyncVar] public Vector2 myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction( [Vector2Pack(1000f, 200f, 0.1f)] Vector2 myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [Vector2Pack(1000f, 200f, 0.1f)] - public Vector2 myValue; - } - - [Serializable] - public struct BitPackStruct - { - [Vector2Pack(1000f, 200f, 0.1f)] - public Vector2 myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Vector2 value = new Vector2(-10.3f, 0.2f); - private const float within = 0.1f; - - private static void AssertValue(Vector2 actual) - { - Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); - Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(27)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(27)); - - AssertValue(clientComponent.myValue); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (27 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"27 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((27 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"27 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(27)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(27)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f.cs.meta b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f.cs.meta deleted file mode 100644 index 3b801887b87..00000000000 --- a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 65f22e8001d898c4b8bf1cbb3146f356 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f2.cs b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f2.cs deleted file mode 100644 index 4cda5f34b03..00000000000 --- a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f2.cs +++ /dev/null @@ -1,173 +0,0 @@ -// DO NOT EDIT: GENERATED BY Vector2PackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.Vector2PackAttributeTests._1000_27f2 -{ - public class BitPackBehaviour : NetworkBehaviour - { - [Vector2Pack(1000f, 200f, 0.1f, 0.1f)] - [SyncVar] public Vector2 myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction( [Vector2Pack(1000f, 200f, 0.1f, 0.1f)] Vector2 myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [Vector2Pack(1000f, 200f, 0.1f, 0.1f)] - public Vector2 myValue; - } - - [Serializable] - public struct BitPackStruct - { - [Vector2Pack(1000f, 200f, 0.1f, 0.1f)] - public Vector2 myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Vector2 value = new Vector2(-10.3f, 0.2f); - private const float within = 0.1f; - - private static void AssertValue(Vector2 actual) - { - Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); - Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(27)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(27)); - - AssertValue(clientComponent.myValue); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (27 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"27 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((27 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"27 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(27)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(27)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f2.cs.meta b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f2.cs.meta deleted file mode 100644 index bedff4db580..00000000000 --- a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f2.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: e72717cde8b02e2409260884fe183589 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_30b.cs b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_30b.cs deleted file mode 100644 index 1e5fd87bb83..00000000000 --- a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_30b.cs +++ /dev/null @@ -1,173 +0,0 @@ -// DO NOT EDIT: GENERATED BY Vector2PackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.Vector2PackAttributeTests._1000_30b -{ - public class BitPackBehaviour : NetworkBehaviour - { - [Vector2Pack(1000f, 200f, 15)] - [SyncVar] public Vector2 myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction( [Vector2Pack(1000f, 200f, 15)] Vector2 myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [Vector2Pack(1000f, 200f, 15)] - public Vector2 myValue; - } - - [Serializable] - public struct BitPackStruct - { - [Vector2Pack(1000f, 200f, 15)] - public Vector2 myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Vector2 value = new Vector2(10.3f, 0.2f); - private const float within = 0.2f; - - private static void AssertValue(Vector2 actual) - { - Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); - Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(30)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(30)); - - AssertValue(clientComponent.myValue); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (30 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"30 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((30 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"30 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(30)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(30)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_30b.cs.meta b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_30b.cs.meta deleted file mode 100644 index 448a779e71c..00000000000 --- a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_30b.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: d36981995f815ab4794b77192fb931fd -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18b2.cs b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18b2.cs deleted file mode 100644 index 598bb55987c..00000000000 --- a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18b2.cs +++ /dev/null @@ -1,173 +0,0 @@ -// DO NOT EDIT: GENERATED BY Vector2PackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.Vector2PackAttributeTests._100_18b2 -{ - public class BitPackBehaviour : NetworkBehaviour - { - [Vector2Pack(100f, 20f, 10, 8)] - [SyncVar] public Vector2 myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction( [Vector2Pack(100f, 20f, 10, 8)] Vector2 myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [Vector2Pack(100f, 20f, 10, 8)] - public Vector2 myValue; - } - - [Serializable] - public struct BitPackStruct - { - [Vector2Pack(100f, 20f, 10, 8)] - public Vector2 myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Vector2 value = new Vector2(-10.3f, 0.2f); - private const float within = 0.2f; - - private static void AssertValue(Vector2 actual) - { - Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); - Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(18)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(18)); - - AssertValue(clientComponent.myValue); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (18 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"18 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((18 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"18 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(18)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(18)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18b2.cs.meta b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18b2.cs.meta deleted file mode 100644 index 587e5954bd1..00000000000 --- a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18b2.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 5258f3f6881332749b165b2287ed73ae -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f.cs b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f.cs deleted file mode 100644 index 66603bbcf93..00000000000 --- a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f.cs +++ /dev/null @@ -1,173 +0,0 @@ -// DO NOT EDIT: GENERATED BY Vector2PackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.Vector2PackAttributeTests._100_18f -{ - public class BitPackBehaviour : NetworkBehaviour - { - [Vector2Pack(100f, 20f, 0.2f)] - [SyncVar] public Vector2 myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction( [Vector2Pack(100f, 20f, 0.2f)] Vector2 myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [Vector2Pack(100f, 20f, 0.2f)] - public Vector2 myValue; - } - - [Serializable] - public struct BitPackStruct - { - [Vector2Pack(100f, 20f, 0.2f)] - public Vector2 myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Vector2 value = new Vector2(10.3f, 0.2f); - private const float within = 0.2f; - - private static void AssertValue(Vector2 actual) - { - Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); - Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(18)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(18)); - - AssertValue(clientComponent.myValue); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (18 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"18 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((18 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"18 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(18)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(18)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f.cs.meta b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f.cs.meta deleted file mode 100644 index a8243b1b398..00000000000 --- a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: c82ec5b521d03244fafbcefa2fd4324b -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f2.cs b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f2.cs deleted file mode 100644 index 3c240b65a69..00000000000 --- a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f2.cs +++ /dev/null @@ -1,173 +0,0 @@ -// DO NOT EDIT: GENERATED BY Vector2PackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.Vector2PackAttributeTests._100_18f2 -{ - public class BitPackBehaviour : NetworkBehaviour - { - [Vector2Pack(100f, 20f, 0.2f, 0.2f)] - [SyncVar] public Vector2 myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction( [Vector2Pack(100f, 20f, 0.2f, 0.2f)] Vector2 myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [Vector2Pack(100f, 20f, 0.2f, 0.2f)] - public Vector2 myValue; - } - - [Serializable] - public struct BitPackStruct - { - [Vector2Pack(100f, 20f, 0.2f, 0.2f)] - public Vector2 myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Vector2 value = new Vector2(10.3f, 0.2f); - private const float within = 0.2f; - - private static void AssertValue(Vector2 actual) - { - Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); - Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(18)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(18)); - - AssertValue(clientComponent.myValue); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (18 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"18 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((18 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"18 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(18)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(18)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f2.cs.meta b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f2.cs.meta deleted file mode 100644 index 59061f9c783..00000000000 --- a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f2.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: ea5a473653e52894db56f3876b478586 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_20b.cs b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_20b.cs deleted file mode 100644 index 5f3c043e3a9..00000000000 --- a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_20b.cs +++ /dev/null @@ -1,173 +0,0 @@ -// DO NOT EDIT: GENERATED BY Vector2PackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.Vector2PackAttributeTests._100_20b -{ - public class BitPackBehaviour : NetworkBehaviour - { - [Vector2Pack(100f, 100f, 10)] - [SyncVar] public Vector2 myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction( [Vector2Pack(100f, 100f, 10)] Vector2 myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [Vector2Pack(100f, 100f, 10)] - public Vector2 myValue; - } - - [Serializable] - public struct BitPackStruct - { - [Vector2Pack(100f, 100f, 10)] - public Vector2 myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Vector2 value = new Vector2(-10.3f, 0.2f); - private const float within = 0.2f; - - private static void AssertValue(Vector2 actual) - { - Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); - Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(20)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(20)); - - AssertValue(clientComponent.myValue); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (20 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"20 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((20 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"20 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(20)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(20)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_20b.cs.meta b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_20b.cs.meta deleted file mode 100644 index 6af056437fc..00000000000 --- a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_20b.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 846700aa697eac74389cd09ec75ba00c -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b.cs b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b.cs deleted file mode 100644 index 0210b8ed64c..00000000000 --- a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b.cs +++ /dev/null @@ -1,173 +0,0 @@ -// DO NOT EDIT: GENERATED BY Vector2PackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.Vector2PackAttributeTests._200_26b -{ - public class BitPackBehaviour : NetworkBehaviour - { - [Vector2Pack(200f, 200f, 13)] - [SyncVar] public Vector2 myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction( [Vector2Pack(200f, 200f, 13)] Vector2 myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [Vector2Pack(200f, 200f, 13)] - public Vector2 myValue; - } - - [Serializable] - public struct BitPackStruct - { - [Vector2Pack(200f, 200f, 13)] - public Vector2 myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Vector2 value = new Vector2(10.3f, 0.2f); - private const float within = 0.2f; - - private static void AssertValue(Vector2 actual) - { - Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); - Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(26)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(26)); - - AssertValue(clientComponent.myValue); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (26 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"26 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((26 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"26 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(26)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(26)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b.cs.meta b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b.cs.meta deleted file mode 100644 index 86ee4309277..00000000000 --- a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: bd35effed30320445859623308ab2f77 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b2.cs b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b2.cs deleted file mode 100644 index 599b11652b5..00000000000 --- a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b2.cs +++ /dev/null @@ -1,173 +0,0 @@ -// DO NOT EDIT: GENERATED BY Vector2PackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.Vector2PackAttributeTests._200_26b2 -{ - public class BitPackBehaviour : NetworkBehaviour - { - [Vector2Pack(200f, 200f, 13, 13)] - [SyncVar] public Vector2 myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction( [Vector2Pack(200f, 200f, 13, 13)] Vector2 myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [Vector2Pack(200f, 200f, 13, 13)] - public Vector2 myValue; - } - - [Serializable] - public struct BitPackStruct - { - [Vector2Pack(200f, 200f, 13, 13)] - public Vector2 myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Vector2 value = new Vector2(10.3f, 0.2f); - private const float within = 0.2f; - - private static void AssertValue(Vector2 actual) - { - Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); - Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(26)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(26)); - - AssertValue(clientComponent.myValue); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (26 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"26 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((26 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"26 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(26)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(26)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b2.cs.meta b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b2.cs.meta deleted file mode 100644 index cbca6300060..00000000000 --- a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b2.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: d103769b00e5b2a418d6760f073f6120 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f.cs b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f.cs deleted file mode 100644 index f857fb78262..00000000000 --- a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f.cs +++ /dev/null @@ -1,173 +0,0 @@ -// DO NOT EDIT: GENERATED BY Vector2PackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.Vector2PackAttributeTests._200_26f -{ - public class BitPackBehaviour : NetworkBehaviour - { - [Vector2Pack(200f, 200f, 0.05f)] - [SyncVar] public Vector2 myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction( [Vector2Pack(200f, 200f, 0.05f)] Vector2 myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [Vector2Pack(200f, 200f, 0.05f)] - public Vector2 myValue; - } - - [Serializable] - public struct BitPackStruct - { - [Vector2Pack(200f, 200f, 0.05f)] - public Vector2 myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Vector2 value = new Vector2(-10.3f, 0.2f); - private const float within = 0.1f; - - private static void AssertValue(Vector2 actual) - { - Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); - Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(26)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(26)); - - AssertValue(clientComponent.myValue); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (26 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"26 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((26 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"26 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(26)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(26)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f.cs.meta b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f.cs.meta deleted file mode 100644 index 2108d118a6c..00000000000 --- a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: fa2265763411aae4f8b3cbfc81326029 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f2.cs b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f2.cs deleted file mode 100644 index 6ebd2c56443..00000000000 --- a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f2.cs +++ /dev/null @@ -1,173 +0,0 @@ -// DO NOT EDIT: GENERATED BY Vector2PackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.Vector2PackAttributeTests._200_26f2 -{ - public class BitPackBehaviour : NetworkBehaviour - { - [Vector2Pack(200f, 200f, 0.05f, 0.05f)] - [SyncVar] public Vector2 myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction( [Vector2Pack(200f, 200f, 0.05f, 0.05f)] Vector2 myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [Vector2Pack(200f, 200f, 0.05f, 0.05f)] - public Vector2 myValue; - } - - [Serializable] - public struct BitPackStruct - { - [Vector2Pack(200f, 200f, 0.05f, 0.05f)] - public Vector2 myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Vector2 value = new Vector2(-10.3f, 0.2f); - private const float within = 0.1f; - - private static void AssertValue(Vector2 actual) - { - Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); - Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(26)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(26)); - - AssertValue(clientComponent.myValue); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (26 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"26 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((26 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"26 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(26)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(26)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f2.cs.meta b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f2.cs.meta deleted file mode 100644 index 9bf1ac58253..00000000000 --- a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f2.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 0703f8afdcf184240a74c61c677fe45b -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector3PackTests.meta b/Assets/Tests/Generated/Vector3PackTests.meta deleted file mode 100644 index d5120f0a329..00000000000 --- a/Assets/Tests/Generated/Vector3PackTests.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: a569ce7d0369c2e428e992a41ec3b569 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42b3.cs b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42b3.cs deleted file mode 100644 index 2b8db6f7403..00000000000 --- a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42b3.cs +++ /dev/null @@ -1,176 +0,0 @@ -// DO NOT EDIT: GENERATED BY Vector3PackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.Vector3PackAttributeTests._1000_42b3 -{ - public class BitPackBehaviour : NetworkBehaviour - { - [Vector3Pack(1000f, 200f, 1000f, 15, 12, 15)] - [SyncVar] public Vector3 myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([Vector3Pack(1000f, 200f, 1000f, 15, 12, 15)] Vector3 myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [Vector3Pack(1000f, 200f, 1000f, 15, 12, 15)] - public Vector3 myValue; - } - - [Serializable] - public struct BitPackStruct - { - [Vector3Pack(1000f, 200f, 1000f, 15, 12, 15)] - public Vector3 myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Vector3 value = new Vector3(10.3f, 0.2f, 20f); - private const float within = 0.2f; - - private static void AssertValue(Vector3 actual) - { - Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); - Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); - Assert.That(actual.z, Is.EqualTo(value.z).Within(within)); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(42)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(42)); - - Assert.That(clientComponent.myValue.x, Is.EqualTo(value.x).Within(within)); - Assert.That(clientComponent.myValue.y, Is.EqualTo(value.y).Within(within)); - Assert.That(clientComponent.myValue.z, Is.EqualTo(value.z).Within(within)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (42 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"42 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((42 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"42 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(42)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(42)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42b3.cs.meta b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42b3.cs.meta deleted file mode 100644 index 516650fa069..00000000000 --- a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42b3.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 4499369b48aebcd4d9e9f49521ab1c89 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f.cs b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f.cs deleted file mode 100644 index 9bff4b22287..00000000000 --- a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f.cs +++ /dev/null @@ -1,176 +0,0 @@ -// DO NOT EDIT: GENERATED BY Vector3PackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.Vector3PackAttributeTests._1000_42f -{ - public class BitPackBehaviour : NetworkBehaviour - { - [Vector3Pack(1000f, 200f, 1000f, 0.1f)] - [SyncVar] public Vector3 myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([Vector3Pack(1000f, 200f, 1000f, 0.1f)] Vector3 myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [Vector3Pack(1000f, 200f, 1000f, 0.1f)] - public Vector3 myValue; - } - - [Serializable] - public struct BitPackStruct - { - [Vector3Pack(1000f, 200f, 1000f, 0.1f)] - public Vector3 myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Vector3 value = new Vector3(-10.3f, 0.2f, 20f); - private const float within = 0.1f; - - private static void AssertValue(Vector3 actual) - { - Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); - Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); - Assert.That(actual.z, Is.EqualTo(value.z).Within(within)); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(42)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(42)); - - Assert.That(clientComponent.myValue.x, Is.EqualTo(value.x).Within(within)); - Assert.That(clientComponent.myValue.y, Is.EqualTo(value.y).Within(within)); - Assert.That(clientComponent.myValue.z, Is.EqualTo(value.z).Within(within)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (42 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"42 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((42 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"42 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(42)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(42)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f.cs.meta b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f.cs.meta deleted file mode 100644 index a346db8c561..00000000000 --- a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 61a4dff1c7f48fa4abca392cea2cf26c -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f3.cs b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f3.cs deleted file mode 100644 index 217a6a5b436..00000000000 --- a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f3.cs +++ /dev/null @@ -1,176 +0,0 @@ -// DO NOT EDIT: GENERATED BY Vector3PackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.Vector3PackAttributeTests._1000_42f3 -{ - public class BitPackBehaviour : NetworkBehaviour - { - [Vector3Pack(1000f, 200f, 1000f, 0.1f, 0.1f, 0.1f)] - [SyncVar] public Vector3 myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([Vector3Pack(1000f, 200f, 1000f, 0.1f, 0.1f, 0.1f)] Vector3 myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [Vector3Pack(1000f, 200f, 1000f, 0.1f, 0.1f, 0.1f)] - public Vector3 myValue; - } - - [Serializable] - public struct BitPackStruct - { - [Vector3Pack(1000f, 200f, 1000f, 0.1f, 0.1f, 0.1f)] - public Vector3 myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Vector3 value = new Vector3(-10.3f, 0.2f, 20f); - private const float within = 0.1f; - - private static void AssertValue(Vector3 actual) - { - Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); - Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); - Assert.That(actual.z, Is.EqualTo(value.z).Within(within)); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(42)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(42)); - - Assert.That(clientComponent.myValue.x, Is.EqualTo(value.x).Within(within)); - Assert.That(clientComponent.myValue.y, Is.EqualTo(value.y).Within(within)); - Assert.That(clientComponent.myValue.z, Is.EqualTo(value.z).Within(within)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (42 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"42 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((42 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"42 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(42)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(42)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f3.cs.meta b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f3.cs.meta deleted file mode 100644 index 718c8ba4270..00000000000 --- a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f3.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: e1eaae3b2239bb04288ae1054b457c3e -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_45b.cs b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_45b.cs deleted file mode 100644 index ee414bf58ec..00000000000 --- a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_45b.cs +++ /dev/null @@ -1,176 +0,0 @@ -// DO NOT EDIT: GENERATED BY Vector3PackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.Vector3PackAttributeTests._1000_45b -{ - public class BitPackBehaviour : NetworkBehaviour - { - [Vector3Pack(1000f, 200f, 1000f, 15)] - [SyncVar] public Vector3 myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([Vector3Pack(1000f, 200f, 1000f, 15)] Vector3 myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [Vector3Pack(1000f, 200f, 1000f, 15)] - public Vector3 myValue; - } - - [Serializable] - public struct BitPackStruct - { - [Vector3Pack(1000f, 200f, 1000f, 15)] - public Vector3 myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Vector3 value = new Vector3(10.3f, 0.2f, 20f); - private const float within = 0.2f; - - private static void AssertValue(Vector3 actual) - { - Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); - Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); - Assert.That(actual.z, Is.EqualTo(value.z).Within(within)); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(45)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(45)); - - Assert.That(clientComponent.myValue.x, Is.EqualTo(value.x).Within(within)); - Assert.That(clientComponent.myValue.y, Is.EqualTo(value.y).Within(within)); - Assert.That(clientComponent.myValue.z, Is.EqualTo(value.z).Within(within)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (45 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"45 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((45 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"45 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(45)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(45)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_45b.cs.meta b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_45b.cs.meta deleted file mode 100644 index 1f55cbec5f7..00000000000 --- a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_45b.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 82e8e82c3e3d5aa40b0f8b17009f95c8 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28b3.cs b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28b3.cs deleted file mode 100644 index bc80e076ffd..00000000000 --- a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28b3.cs +++ /dev/null @@ -1,176 +0,0 @@ -// DO NOT EDIT: GENERATED BY Vector3PackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.Vector3PackAttributeTests._100_28b3 -{ - public class BitPackBehaviour : NetworkBehaviour - { - [Vector3Pack(100f, 20f, 100f, 10, 8, 10)] - [SyncVar] public Vector3 myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([Vector3Pack(100f, 20f, 100f, 10, 8, 10)] Vector3 myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [Vector3Pack(100f, 20f, 100f, 10, 8, 10)] - public Vector3 myValue; - } - - [Serializable] - public struct BitPackStruct - { - [Vector3Pack(100f, 20f, 100f, 10, 8, 10)] - public Vector3 myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Vector3 value = new Vector3(-10.3f, 0.2f, 20f); - private const float within = 0.2f; - - private static void AssertValue(Vector3 actual) - { - Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); - Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); - Assert.That(actual.z, Is.EqualTo(value.z).Within(within)); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(28)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(28)); - - Assert.That(clientComponent.myValue.x, Is.EqualTo(value.x).Within(within)); - Assert.That(clientComponent.myValue.y, Is.EqualTo(value.y).Within(within)); - Assert.That(clientComponent.myValue.z, Is.EqualTo(value.z).Within(within)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (28 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"28 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((28 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"28 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(28)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(28)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28b3.cs.meta b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28b3.cs.meta deleted file mode 100644 index ad8ffff1d92..00000000000 --- a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28b3.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: df73554e4d468d44fb5d363a7871f5b5 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f.cs b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f.cs deleted file mode 100644 index 786ca9671e2..00000000000 --- a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f.cs +++ /dev/null @@ -1,176 +0,0 @@ -// DO NOT EDIT: GENERATED BY Vector3PackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.Vector3PackAttributeTests._100_28f -{ - public class BitPackBehaviour : NetworkBehaviour - { - [Vector3Pack(100f, 20f, 100f, 0.2f)] - [SyncVar] public Vector3 myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([Vector3Pack(100f, 20f, 100f, 0.2f)] Vector3 myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [Vector3Pack(100f, 20f, 100f, 0.2f)] - public Vector3 myValue; - } - - [Serializable] - public struct BitPackStruct - { - [Vector3Pack(100f, 20f, 100f, 0.2f)] - public Vector3 myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Vector3 value = new Vector3(10.3f, 0.2f, 20f); - private const float within = 0.2f; - - private static void AssertValue(Vector3 actual) - { - Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); - Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); - Assert.That(actual.z, Is.EqualTo(value.z).Within(within)); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(28)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(28)); - - Assert.That(clientComponent.myValue.x, Is.EqualTo(value.x).Within(within)); - Assert.That(clientComponent.myValue.y, Is.EqualTo(value.y).Within(within)); - Assert.That(clientComponent.myValue.z, Is.EqualTo(value.z).Within(within)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (28 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"28 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((28 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"28 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(28)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(28)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f.cs.meta b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f.cs.meta deleted file mode 100644 index e79c15a5926..00000000000 --- a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 201a148d2fce2454fada358a9f3572c4 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f3.cs b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f3.cs deleted file mode 100644 index 24c831f6953..00000000000 --- a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f3.cs +++ /dev/null @@ -1,176 +0,0 @@ -// DO NOT EDIT: GENERATED BY Vector3PackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.Vector3PackAttributeTests._100_28f3 -{ - public class BitPackBehaviour : NetworkBehaviour - { - [Vector3Pack(100f, 20f, 100f, 0.2f, 0.2f, 0.2f)] - [SyncVar] public Vector3 myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([Vector3Pack(100f, 20f, 100f, 0.2f, 0.2f, 0.2f)] Vector3 myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [Vector3Pack(100f, 20f, 100f, 0.2f, 0.2f, 0.2f)] - public Vector3 myValue; - } - - [Serializable] - public struct BitPackStruct - { - [Vector3Pack(100f, 20f, 100f, 0.2f, 0.2f, 0.2f)] - public Vector3 myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Vector3 value = new Vector3(10.3f, 0.2f, 20f); - private const float within = 0.2f; - - private static void AssertValue(Vector3 actual) - { - Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); - Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); - Assert.That(actual.z, Is.EqualTo(value.z).Within(within)); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(28)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(28)); - - Assert.That(clientComponent.myValue.x, Is.EqualTo(value.x).Within(within)); - Assert.That(clientComponent.myValue.y, Is.EqualTo(value.y).Within(within)); - Assert.That(clientComponent.myValue.z, Is.EqualTo(value.z).Within(within)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (28 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"28 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((28 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"28 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(28)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(28)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f3.cs.meta b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f3.cs.meta deleted file mode 100644 index 831a4f25261..00000000000 --- a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f3.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: adef600428bbf1544b685f42f50180a9 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_30b.cs b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_30b.cs deleted file mode 100644 index 624f3ead925..00000000000 --- a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_30b.cs +++ /dev/null @@ -1,176 +0,0 @@ -// DO NOT EDIT: GENERATED BY Vector3PackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.Vector3PackAttributeTests._100_30b -{ - public class BitPackBehaviour : NetworkBehaviour - { - [Vector3Pack(100f, 100f, 100f, 10)] - [SyncVar] public Vector3 myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([Vector3Pack(100f, 100f, 100f, 10)] Vector3 myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [Vector3Pack(100f, 100f, 100f, 10)] - public Vector3 myValue; - } - - [Serializable] - public struct BitPackStruct - { - [Vector3Pack(100f, 100f, 100f, 10)] - public Vector3 myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Vector3 value = new Vector3(-10.3f, 0.2f, 20f); - private const float within = 0.2f; - - private static void AssertValue(Vector3 actual) - { - Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); - Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); - Assert.That(actual.z, Is.EqualTo(value.z).Within(within)); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(30)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(30)); - - Assert.That(clientComponent.myValue.x, Is.EqualTo(value.x).Within(within)); - Assert.That(clientComponent.myValue.y, Is.EqualTo(value.y).Within(within)); - Assert.That(clientComponent.myValue.z, Is.EqualTo(value.z).Within(within)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (30 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"30 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((30 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"30 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(30)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(30)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_30b.cs.meta b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_30b.cs.meta deleted file mode 100644 index bc5b62451e5..00000000000 --- a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_30b.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 4efe76aa2210be54385ba6d4793bed9a -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b.cs b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b.cs deleted file mode 100644 index 462057d4646..00000000000 --- a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b.cs +++ /dev/null @@ -1,176 +0,0 @@ -// DO NOT EDIT: GENERATED BY Vector3PackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.Vector3PackAttributeTests._200_39b -{ - public class BitPackBehaviour : NetworkBehaviour - { - [Vector3Pack(200f, 200f, 200f, 13)] - [SyncVar] public Vector3 myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([Vector3Pack(200f, 200f, 200f, 13)] Vector3 myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [Vector3Pack(200f, 200f, 200f, 13)] - public Vector3 myValue; - } - - [Serializable] - public struct BitPackStruct - { - [Vector3Pack(200f, 200f, 200f, 13)] - public Vector3 myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Vector3 value = new Vector3(10.3f, 0.2f, 20f); - private const float within = 0.2f; - - private static void AssertValue(Vector3 actual) - { - Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); - Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); - Assert.That(actual.z, Is.EqualTo(value.z).Within(within)); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(39)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(39)); - - Assert.That(clientComponent.myValue.x, Is.EqualTo(value.x).Within(within)); - Assert.That(clientComponent.myValue.y, Is.EqualTo(value.y).Within(within)); - Assert.That(clientComponent.myValue.z, Is.EqualTo(value.z).Within(within)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (39 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"39 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((39 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"39 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(39)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(39)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b.cs.meta b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b.cs.meta deleted file mode 100644 index dcfbbe6b70c..00000000000 --- a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 769801aa4625b6741b04b8cfea643166 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b3.cs b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b3.cs deleted file mode 100644 index 05437b5579e..00000000000 --- a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b3.cs +++ /dev/null @@ -1,176 +0,0 @@ -// DO NOT EDIT: GENERATED BY Vector3PackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.Vector3PackAttributeTests._200_39b3 -{ - public class BitPackBehaviour : NetworkBehaviour - { - [Vector3Pack(200f, 200f, 200f, 13, 13, 13)] - [SyncVar] public Vector3 myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([Vector3Pack(200f, 200f, 200f, 13, 13, 13)] Vector3 myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [Vector3Pack(200f, 200f, 200f, 13, 13, 13)] - public Vector3 myValue; - } - - [Serializable] - public struct BitPackStruct - { - [Vector3Pack(200f, 200f, 200f, 13, 13, 13)] - public Vector3 myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Vector3 value = new Vector3(10.3f, 0.2f, 20f); - private const float within = 0.2f; - - private static void AssertValue(Vector3 actual) - { - Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); - Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); - Assert.That(actual.z, Is.EqualTo(value.z).Within(within)); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(39)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(39)); - - Assert.That(clientComponent.myValue.x, Is.EqualTo(value.x).Within(within)); - Assert.That(clientComponent.myValue.y, Is.EqualTo(value.y).Within(within)); - Assert.That(clientComponent.myValue.z, Is.EqualTo(value.z).Within(within)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (39 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"39 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((39 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"39 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(39)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(39)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b3.cs.meta b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b3.cs.meta deleted file mode 100644 index 3274ebbae55..00000000000 --- a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b3.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 1befa91894c23e34fa364fadf7d44ab9 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f.cs b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f.cs deleted file mode 100644 index 7a7f9af753a..00000000000 --- a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f.cs +++ /dev/null @@ -1,176 +0,0 @@ -// DO NOT EDIT: GENERATED BY Vector3PackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.Vector3PackAttributeTests._200_39f -{ - public class BitPackBehaviour : NetworkBehaviour - { - [Vector3Pack(200f, 200f, 200f, 0.05f)] - [SyncVar] public Vector3 myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([Vector3Pack(200f, 200f, 200f, 0.05f)] Vector3 myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [Vector3Pack(200f, 200f, 200f, 0.05f)] - public Vector3 myValue; - } - - [Serializable] - public struct BitPackStruct - { - [Vector3Pack(200f, 200f, 200f, 0.05f)] - public Vector3 myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Vector3 value = new Vector3(-10.3f, 0.2f, -20f); - private const float within = 0.1f; - - private static void AssertValue(Vector3 actual) - { - Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); - Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); - Assert.That(actual.z, Is.EqualTo(value.z).Within(within)); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(39)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(39)); - - Assert.That(clientComponent.myValue.x, Is.EqualTo(value.x).Within(within)); - Assert.That(clientComponent.myValue.y, Is.EqualTo(value.y).Within(within)); - Assert.That(clientComponent.myValue.z, Is.EqualTo(value.z).Within(within)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (39 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"39 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((39 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"39 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(39)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(39)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f.cs.meta b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f.cs.meta deleted file mode 100644 index 63cc9e7c093..00000000000 --- a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 96c1c96f45ce08c48afcb836421a3fb8 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f3.cs b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f3.cs deleted file mode 100644 index 9a67cbc182b..00000000000 --- a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f3.cs +++ /dev/null @@ -1,176 +0,0 @@ -// DO NOT EDIT: GENERATED BY Vector3PackTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.Vector3PackAttributeTests._200_39f3 -{ - public class BitPackBehaviour : NetworkBehaviour - { - [Vector3Pack(200f, 200f, 200f, 0.05f, 0.05f, 0.05f)] - [SyncVar] public Vector3 myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([Vector3Pack(200f, 200f, 200f, 0.05f, 0.05f, 0.05f)] Vector3 myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [Vector3Pack(200f, 200f, 200f, 0.05f, 0.05f, 0.05f)] - public Vector3 myValue; - } - - [Serializable] - public struct BitPackStruct - { - [Vector3Pack(200f, 200f, 200f, 0.05f, 0.05f, 0.05f)] - public Vector3 myValue; - } - - public class BitPackTest : ClientServerSetup - { - private static readonly Vector3 value = new Vector3(-10.3f, 0.2f, -20f); - private const float within = 0.1f; - - private static void AssertValue(Vector3 actual) - { - Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); - Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); - Assert.That(actual.z, Is.EqualTo(value.z).Within(within)); - } - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(39)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(39)); - - Assert.That(clientComponent.myValue.x, Is.EqualTo(value.x).Within(within)); - Assert.That(clientComponent.myValue.y, Is.EqualTo(value.y).Within(within)); - Assert.That(clientComponent.myValue.z, Is.EqualTo(value.z).Within(within)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - AssertValue(v); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (39 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"39 bits is %%PAYLOAD_SIZE%% bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((39 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"39 bits is {expectedPayLoadSize - 2} bytes in payload"); - AssertValue(outMessage.myValue); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(39)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(39)); - - AssertValue(outStruct.myValue); - } - } - } - } -} diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f3.cs.meta b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f3.cs.meta deleted file mode 100644 index a545c7db462..00000000000 --- a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f3.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: f449b59b1825478419f46284820806c3 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/ZigZagTests.meta b/Assets/Tests/Generated/ZigZagTests.meta deleted file mode 100644 index 7405489ec7f..00000000000 --- a/Assets/Tests/Generated/ZigZagTests.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 21a2a3c1512fe0b42af86e8b54ba7a33 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum2_4_negative.cs b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum2_4_negative.cs deleted file mode 100644 index dae6b3678d2..00000000000 --- a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum2_4_negative.cs +++ /dev/null @@ -1,173 +0,0 @@ -// DO NOT EDIT: GENERATED BY ZigZagTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.ZigZagAttributeTests.MyEnum2_4_negative -{ - [System.Serializable] - public enum MyEnum2 - { - Negative = -1, - Zero = 0, - Positive = 1, - } - public class BitPackBehaviour : NetworkBehaviour - { - [BitCount(4), ZigZagEncode] - [SyncVar] public MyEnum2 myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCount(4), ZigZagEncode] MyEnum2 myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCount(4), ZigZagEncode] - public MyEnum2 myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCount(4), ZigZagEncode] - public MyEnum2 myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const MyEnum2 value = (MyEnum2)(-1); - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(4)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(4)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (4 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"4 bits is 1 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((4 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"4 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(4)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(4)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum2_4_negative.cs.meta b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum2_4_negative.cs.meta deleted file mode 100644 index a43e72c5c8a..00000000000 --- a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum2_4_negative.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 93d454d332b06d34589a44dc7704fa83 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum_4.cs b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum_4.cs deleted file mode 100644 index e7c730e7ef5..00000000000 --- a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum_4.cs +++ /dev/null @@ -1,173 +0,0 @@ -// DO NOT EDIT: GENERATED BY ZigZagTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.ZigZagAttributeTests.MyEnum_4 -{ - [System.Serializable] - public enum MyEnum - { - Negative = -1, - Zero = 0, - Positive = 1, - } - public class BitPackBehaviour : NetworkBehaviour - { - [BitCount(4), ZigZagEncode] - [SyncVar] public MyEnum myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCount(4), ZigZagEncode] MyEnum myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCount(4), ZigZagEncode] - public MyEnum myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCount(4), ZigZagEncode] - public MyEnum myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const MyEnum value = (MyEnum)1; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(4)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(4)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (4 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"4 bits is 1 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((4 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"4 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(4)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(4)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum_4.cs.meta b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum_4.cs.meta deleted file mode 100644 index 0c652b659fb..00000000000 --- a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum_4.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 8352249ab3b565e4daa89d65f9afb9ca -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10.cs b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10.cs deleted file mode 100644 index 6c729766e3c..00000000000 --- a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY ZigZagTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.ZigZagAttributeTests.int_10 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [BitCount(10), ZigZagEncode] - [SyncVar] public int myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCount(10), ZigZagEncode] int myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCount(10), ZigZagEncode] - public int myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCount(10), ZigZagEncode] - public int myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const int value = 100; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(10)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(10)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (10 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is 2 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((10 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(10)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(10)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10.cs.meta b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10.cs.meta deleted file mode 100644 index c31d6b6ab34..00000000000 --- a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 6acef4578f12518468496121bf1df8de -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10_negative.cs b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10_negative.cs deleted file mode 100644 index 9e23e3e8369..00000000000 --- a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10_negative.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY ZigZagTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.ZigZagAttributeTests.int_10_negative -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [BitCount(10), ZigZagEncode] - [SyncVar] public int myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCount(10), ZigZagEncode] int myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCount(10), ZigZagEncode] - public int myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCount(10), ZigZagEncode] - public int myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const int value = -25; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(10)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(10)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (10 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is 2 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((10 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(10)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(10)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10_negative.cs.meta b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10_negative.cs.meta deleted file mode 100644 index 9176e8f0b2c..00000000000 --- a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10_negative.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 42b348f9e44bab244878c17bfec84701 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10.cs b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10.cs deleted file mode 100644 index d0fa021f858..00000000000 --- a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY ZigZagTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.ZigZagAttributeTests.long_10 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [BitCount(10), ZigZagEncode] - [SyncVar] public long myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCount(10), ZigZagEncode] long myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCount(10), ZigZagEncode] - public long myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCount(10), ZigZagEncode] - public long myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const long value = 14; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(10)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(10)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (10 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is 2 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((10 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(10)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(10)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10.cs.meta b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10.cs.meta deleted file mode 100644 index b5d11ac3119..00000000000 --- a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 172d661bb687b6d49a7ab94b3417bafc -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10_negative.cs b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10_negative.cs deleted file mode 100644 index a62d30d092e..00000000000 --- a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10_negative.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY ZigZagTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.ZigZagAttributeTests.long_10_negative -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [BitCount(10), ZigZagEncode] - [SyncVar] public long myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCount(10), ZigZagEncode] long myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCount(10), ZigZagEncode] - public long myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCount(10), ZigZagEncode] - public long myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const long value = -30; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(10)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(10)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (10 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is 2 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((10 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(10)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(10)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10_negative.cs.meta b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10_negative.cs.meta deleted file mode 100644 index 9ca75464db6..00000000000 --- a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10_negative.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 43ae8061a6b6d9442a52e73c698b2cd4 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10.cs b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10.cs deleted file mode 100644 index aef487bd0fe..00000000000 --- a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY ZigZagTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.ZigZagAttributeTests.short_10 -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [BitCount(10), ZigZagEncode] - [SyncVar] public short myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCount(10), ZigZagEncode] short myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCount(10), ZigZagEncode] - public short myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCount(10), ZigZagEncode] - public short myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const short value = 15; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(10)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(10)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (10 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is 2 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((10 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(10)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(10)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10.cs.meta b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10.cs.meta deleted file mode 100644 index a956d669b9c..00000000000 --- a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: c026d578164f23c41a2419a1d42e07cc -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10_negative.cs b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10_negative.cs deleted file mode 100644 index 80609f92b27..00000000000 --- a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10_negative.cs +++ /dev/null @@ -1,167 +0,0 @@ -// DO NOT EDIT: GENERATED BY ZigZagTestGenerator.cs - -using System; -using System.Collections; -using Mirage.RemoteCalls; -using Mirage.Serialization; -using Mirage.Tests.Runtime.ClientServer; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Mirage.Tests.Runtime.Generated.ZigZagAttributeTests.short_10_negative -{ - - public class BitPackBehaviour : NetworkBehaviour - { - [BitCount(10), ZigZagEncode] - [SyncVar] public short myValue; - - public event Action onRpc; - - [ClientRpc] - public void RpcSomeFunction([BitCount(10), ZigZagEncode] short myParam) - { - onRpc?.Invoke(myParam); - } - - // Use BitPackStruct in rpc so it has writer generated - [ClientRpc] - public void RpcOtherFunction(BitPackStruct myParam) - { - // nothing - } - } - - [NetworkMessage] - public struct BitPackMessage - { - [BitCount(10), ZigZagEncode] - public short myValue; - } - - [Serializable] - public struct BitPackStruct - { - [BitCount(10), ZigZagEncode] - public short myValue; - } - - public class BitPackTest : ClientServerSetup - { - private const short value = -20; - - [Test] - public void SyncVarIsBitPacked() - { - serverComponent.myValue = value; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - serverComponent.SerializeSyncVars(writer, true); - - Assert.That(writer.BitPosition, Is.EqualTo(10)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - clientComponent.DeserializeSyncVars(reader, true); - Assert.That(reader.BitPosition, Is.EqualTo(10)); - - Assert.That(clientComponent.myValue, Is.EqualTo(value)); - } - } - } - - [UnityTest] - public IEnumerator RpcIsBitPacked() - { - int called = 0; - clientComponent.onRpc += (v) => - { - called++; - Assert.That(v, Is.EqualTo(value)); - }; - - client.MessageHandler.UnregisterHandler(); - int payloadSize = 0; - client.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - payloadSize = msg.Payload.Count; - clientObjectManager._rpcHandler.OnRpcMessage(player, msg); - }); - - serverComponent.RpcSomeFunction(value); - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - - // this will round up to nearest 8 - int expectedPayLoadSize = (10 + 7) / 8; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is 2 bytes in payload"); - } - - [UnityTest] - public IEnumerator StructIsBitPacked() - { - var inMessage = new BitPackMessage - { - myValue = value, - }; - - int payloadSize = 0; - int called = 0; - BitPackMessage outMessage = default; - server.MessageHandler.RegisterHandler((player, msg) => - { - // store value in variable because assert will throw and be catch by message wrapper - called++; - outMessage = msg; - }); - Action diagAction = (info) => - { - if (info.message is BitPackMessage) - { - payloadSize = info.bytes; - } - }; - - NetworkDiagnostics.OutMessageEvent += diagAction; - client.Player.Send(inMessage); - NetworkDiagnostics.OutMessageEvent -= diagAction; - yield return null; - yield return null; - Assert.That(called, Is.EqualTo(1)); - // this will round up to nearest 8 - // +2 for message header - int expectedPayLoadSize = ((10 + 7) / 8) + 2; - Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is {expectedPayLoadSize - 2} bytes in payload"); - Assert.That(outMessage, Is.EqualTo(inMessage)); - } - - [Test] - public void MessageIsBitPacked() - { - var inStruct = new BitPackStruct - { - myValue = value, - }; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // generic write, uses generated function that should include bitPacking - writer.Write(inStruct); - - Assert.That(writer.BitPosition, Is.EqualTo(10)); - - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) - { - var outStruct = reader.Read(); - Assert.That(reader.BitPosition, Is.EqualTo(10)); - - Assert.That(outStruct, Is.EqualTo(inStruct)); - } - } - } - } -} diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10_negative.cs.meta b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10_negative.cs.meta deleted file mode 100644 index d1146b071b9..00000000000 --- a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10_negative.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 08c0171828b97284c9b3caf4b2e128eb -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Performance/Runtime/10K/Scripts/Health.cs b/Assets/Tests/Performance/Runtime/10K/Scripts/Health.cs index 0feb3148d61..a76c6190931 100644 --- a/Assets/Tests/Performance/Runtime/10K/Scripts/Health.cs +++ b/Assets/Tests/Performance/Runtime/10K/Scripts/Health.cs @@ -2,7 +2,7 @@ namespace Mirage.Examples { public class Health : NetworkBehaviour { - [SyncVar] public int health = 10; + [SyncVar] public int health { get; set; } = 10; [Server(error = false)] public void Update() diff --git a/Assets/Tests/Performance/Runtime/10KL/Scripts/Health.cs b/Assets/Tests/Performance/Runtime/10KL/Scripts/Health.cs index f0fec55afcd..3a6861b7f30 100644 --- a/Assets/Tests/Performance/Runtime/10KL/Scripts/Health.cs +++ b/Assets/Tests/Performance/Runtime/10KL/Scripts/Health.cs @@ -5,7 +5,7 @@ namespace Mirage.Examples.Light { public class Health : NetworkBehaviour { - [SyncVar] public int health = 10; + [SyncVar] public int health { get; set; } = 10; private void Awake() { Identity.OnStartServer.AddListener(OnStartServer); diff --git a/Assets/Tests/Performance/Runtime/MultipleClients/Scripts/MonsterBehavior.cs b/Assets/Tests/Performance/Runtime/MultipleClients/Scripts/MonsterBehavior.cs index e91dce22a81..50e2560675b 100644 --- a/Assets/Tests/Performance/Runtime/MultipleClients/Scripts/MonsterBehavior.cs +++ b/Assets/Tests/Performance/Runtime/MultipleClients/Scripts/MonsterBehavior.cs @@ -1,4 +1,4 @@ -using System.Collections; +using System.Collections; using UnityEngine; namespace Mirage.Tests.Performance.Runtime @@ -6,10 +6,10 @@ namespace Mirage.Tests.Performance.Runtime public class MonsterBehavior : NetworkBehaviour { [SyncVar] - public Vector3 position; + public Vector3 position { get; set; } [SyncVar] - public int MonsterId; + public int MonsterId { get; set; } public void Awake() { diff --git a/Assets/Tests/Performance/Runtime/NetworkIdentity/NetworkIdentityPerformance.cs b/Assets/Tests/Performance/Runtime/NetworkIdentity/NetworkIdentityPerformance.cs index 7988444b3b2..b046fc3d01f 100644 --- a/Assets/Tests/Performance/Runtime/NetworkIdentity/NetworkIdentityPerformance.cs +++ b/Assets/Tests/Performance/Runtime/NetworkIdentity/NetworkIdentityPerformance.cs @@ -7,7 +7,7 @@ namespace Mirage.Tests.Performance { public class Health : NetworkBehaviour { - [SyncVar] public int health = 10; + [SyncVar] public int health { get; set; } = 10; public void Update() { diff --git a/Assets/Tests/Runtime/ClientServer/Generics/WithGenericSyncVarEvent.cs b/Assets/Tests/Runtime/ClientServer/Generics/WithGenericSyncVarEvent.cs index d460e9da8e9..166fcfc2b67 100644 --- a/Assets/Tests/Runtime/ClientServer/Generics/WithGenericSyncVarEvent.cs +++ b/Assets/Tests/Runtime/ClientServer/Generics/WithGenericSyncVarEvent.cs @@ -11,7 +11,7 @@ public class WithGenericSyncVarEvent_behaviour : NetworkBehaviour public event Action hook; [SyncVar(hook = nameof(hook))] - public T value; + public T value { get; set; } } public class WithGenericSyncVarEvent_behaviourInt : WithGenericSyncVarEvent_behaviour diff --git a/Assets/Tests/Runtime/ClientServer/Generics/WithGenericSyncVarHook.cs b/Assets/Tests/Runtime/ClientServer/Generics/WithGenericSyncVarHook.cs index 7a11847287d..fc764cabf3d 100644 --- a/Assets/Tests/Runtime/ClientServer/Generics/WithGenericSyncVarHook.cs +++ b/Assets/Tests/Runtime/ClientServer/Generics/WithGenericSyncVarHook.cs @@ -11,7 +11,7 @@ public class WithGenericSyncVarHook_behaviour : NetworkBehaviour public event Action hookCalled; [SyncVar(hook = nameof(onValueChanged))] - public T value; + public T value { get; set; } private void onValueChanged(T oldValue, T newValue) { diff --git a/Assets/Tests/Runtime/ClientServer/NetworkIdentitySyncvarTest.cs b/Assets/Tests/Runtime/ClientServer/NetworkIdentitySyncvarTest.cs index 838953ea962..1d763140b54 100644 --- a/Assets/Tests/Runtime/ClientServer/NetworkIdentitySyncvarTest.cs +++ b/Assets/Tests/Runtime/ClientServer/NetworkIdentitySyncvarTest.cs @@ -8,7 +8,7 @@ namespace Mirage.Tests.Runtime.ClientServer public class SampleBehaviorWithNI : NetworkBehaviour { [SyncVar(hook = nameof(OnTargetChange))] - public NetworkIdentity target; + public NetworkIdentity target { get; set; } public void OnTargetChange(NetworkIdentity _, NetworkIdentity networkIdentity) { diff --git a/Assets/Tests/Runtime/ClientServer/SyncVarEventHook.cs b/Assets/Tests/Runtime/ClientServer/SyncVarEventHook.cs index 1512cfa12e7..e4f9b378504 100644 --- a/Assets/Tests/Runtime/ClientServer/SyncVarEventHook.cs +++ b/Assets/Tests/Runtime/ClientServer/SyncVarEventHook.cs @@ -8,7 +8,7 @@ namespace Mirage.Tests.Runtime.ClientServer public class BehaviourWithSyncVarEvent : NetworkBehaviour { [SyncVar(hook = nameof(OnHealthChanged))] - public int health; + public int health { get; set; } public event Action OnHealthChanged; } diff --git a/Assets/Tests/Runtime/ClientServer/SyncVarFireHookOnServerTests.cs b/Assets/Tests/Runtime/ClientServer/SyncVarFireHookOnServerTests.cs index 2c9c71b6dde..6c101322408 100644 --- a/Assets/Tests/Runtime/ClientServer/SyncVarFireHookOnServerTests.cs +++ b/Assets/Tests/Runtime/ClientServer/SyncVarFireHookOnServerTests.cs @@ -9,7 +9,7 @@ namespace Mirage.Tests.Runtime.ClientServer public class BehaviourWithSyncVarOnServerEvent : NetworkBehaviour { [SyncVar(hook = nameof(OnHealthChanged), invokeHookOnServer = true)] - public int health; + public int health { get; set; } public event Action OnHealthChanged; } @@ -17,7 +17,7 @@ public class BehaviourWithSyncVarOnServerEvent : NetworkBehaviour public class BehaviourWithSyncVarOnServerMethod : NetworkBehaviour { [SyncVar(hook = nameof(OnHealthChanged), invokeHookOnServer = true)] - public int health; + public int health { get; set; } public int called = 0; diff --git a/Assets/Tests/Runtime/ClientServer/SyncVarHooks/SyncVarHookArgCountTests.cs b/Assets/Tests/Runtime/ClientServer/SyncVarHooks/SyncVarHookArgCountTests.cs index 60760577511..34de811ee03 100644 --- a/Assets/Tests/Runtime/ClientServer/SyncVarHooks/SyncVarHookArgCountTests.cs +++ b/Assets/Tests/Runtime/ClientServer/SyncVarHooks/SyncVarHookArgCountTests.cs @@ -10,7 +10,7 @@ public class SyncVarHookWith0ArgBehaviour : NetworkBehaviour { public event Action onChangedCalled; - [SyncVar(hook = nameof(OnChange))] public int var; + [SyncVar(hook = nameof(OnChange))] public int var { get; set; } private void OnChange() { @@ -21,14 +21,14 @@ private void OnChange() public class SyncVarHookWith0ArgEventBehaviour : NetworkBehaviour { public event Action OnChange; - [SyncVar(hook = nameof(OnChange))] public int var; + [SyncVar(hook = nameof(OnChange))] public int var { get; set; } } public class SyncVarHookWith1ArgBehaviour : NetworkBehaviour { public event Action onChangedCalled; - [SyncVar(hook = nameof(OnChange))] public int var; + [SyncVar(hook = nameof(OnChange))] public int var { get; set; } private void OnChange(int newValue) { @@ -39,7 +39,7 @@ private void OnChange(int newValue) public class SyncVarHookWith1ArgEventBehaviour : NetworkBehaviour { public event Action OnChange; - [SyncVar(hook = nameof(OnChange))] public int var; + [SyncVar(hook = nameof(OnChange))] public int var { get; set; } } public class SyncVarHookMethod0ArgWithOverLoadBehaviour : NetworkBehaviour @@ -47,7 +47,7 @@ public class SyncVarHookMethod0ArgWithOverLoadBehaviour : NetworkBehaviour public event Action onChangedCalled; [SyncVar(hook = nameof(OnChange), hookType = SyncHookType.MethodWith0Arg)] - public int var; + public int var { get; set; } private void OnChange() { @@ -72,7 +72,7 @@ public class SyncVarHookMethod1ArgWithOverLoadBehaviour : NetworkBehaviour public event Action onChangedCalled; [SyncVar(hook = nameof(OnChange), hookType = SyncHookType.MethodWith1Arg)] - public int var; + public int var { get; set; } private void OnChange(int newValue) { @@ -90,7 +90,7 @@ public class SyncVarHookMethod2ArgWithOverLoadBehaviour : NetworkBehaviour public event Action onChangedCalled; [SyncVar(hook = nameof(OnChange), hookType = SyncHookType.MethodWith2Arg)] - public int var; + public int var { get; set; } private void OnChange(int newValue) { diff --git a/Assets/Tests/Runtime/Serialization/NetworkBehaviourSerializeTest.cs b/Assets/Tests/Runtime/Serialization/NetworkBehaviourSerializeTest.cs index 391d3c26b5c..5bbfa44ec5b 100644 --- a/Assets/Tests/Runtime/Serialization/NetworkBehaviourSerializeTest.cs +++ b/Assets/Tests/Runtime/Serialization/NetworkBehaviourSerializeTest.cs @@ -12,7 +12,7 @@ internal abstract class AbstractBehaviour : NetworkBehaviour public readonly SyncList syncListInAbstract = new SyncList(); [SyncVar] - public int SyncFieldInAbstract; + public int SyncFieldInAbstract { get; set; } } internal class BehaviourWithSyncVar : NetworkBehaviour @@ -20,7 +20,7 @@ internal class BehaviourWithSyncVar : NetworkBehaviour public readonly SyncList syncList = new SyncList(); [SyncVar] - public int SyncField; + public int SyncField { get; set; } } internal class OverrideBehaviourFromSyncVar : AbstractBehaviour @@ -33,7 +33,7 @@ internal class OverrideBehaviourWithSyncVarFromSyncVar : AbstractBehaviour public readonly SyncList syncListInOverride = new SyncList(); [SyncVar] - public int SyncFieldInOverride; + public int SyncFieldInOverride { get; set; } } internal class MiddleClass : AbstractBehaviour @@ -46,14 +46,14 @@ internal class SubClass : MiddleClass // class with sync var // this is to make sure that override works correctly if base class doesnt have sync vars [SyncVar] - public Vector3 anotherSyncField; + public Vector3 anotherSyncField { get; set; } } internal class MiddleClassWithSyncVar : AbstractBehaviour { // class with sync var [SyncVar] - public string syncFieldInMiddle; + public string syncFieldInMiddle { get; set; } } internal class SubClassFromSyncVar : MiddleClassWithSyncVar @@ -61,7 +61,7 @@ internal class SubClassFromSyncVar : MiddleClassWithSyncVar // class with sync var // this is to make sure that override works correctly if base class doesnt have sync vars [SyncVar] - public Vector3 syncFieldInSub; + public Vector3 syncFieldInSub { get; set; } } #endregion @@ -73,7 +73,7 @@ internal class BehaviourWithSyncVarWithOnSerialize : NetworkBehaviour public readonly SyncList syncList = new SyncList(); [SyncVar] - public int SyncField; + public int SyncField { get; set; } public float customSerializeField; @@ -110,7 +110,7 @@ internal class OverrideBehaviourWithSyncVarFromSyncVarWithOnSerialize : Abstract public readonly SyncList syncListInOverride = new SyncList(); [SyncVar] - public int SyncFieldInOverride; + public int SyncFieldInOverride { get; set; } public float customSerializeField; diff --git a/Assets/Tests/Runtime/Syncing/SyncDirectionFromOwnerHook.cs b/Assets/Tests/Runtime/Syncing/SyncDirectionFromOwnerHook.cs index 030a2541adb..fbc720e29f9 100644 --- a/Assets/Tests/Runtime/Syncing/SyncDirectionFromOwnerHook.cs +++ b/Assets/Tests/Runtime/Syncing/SyncDirectionFromOwnerHook.cs @@ -10,19 +10,19 @@ namespace Mirage.Tests.Runtime.Syncing public class MockPlayerInvokeHooks : NetworkBehaviour { [SyncVar(hook = nameof(EventServerOwner), invokeHookOnServer = true, invokeHookOnOwner = true)] - public int FieldServerOwner; + public int FieldServerOwner { get; set; } public event Action EventServerOwner; [SyncVar(hook = nameof(EventServer), invokeHookOnServer = true, invokeHookOnOwner = false)] - public int FieldServer; + public int FieldServer { get; set; } public event Action EventServer; [SyncVar(hook = nameof(EventOwner), invokeHookOnServer = false, invokeHookOnOwner = true)] - public int FieldOwner; + public int FieldOwner { get; set; } public event Action EventOwner; [SyncVar(hook = nameof(EventNone), invokeHookOnServer = false, invokeHookOnOwner = false)] - public int FieldNone; + public int FieldNone { get; set; } public event Action EventNone; } diff --git a/Assets/Tests/Runtime/Syncing/SyncDirectionFromServerHostToOwner.cs b/Assets/Tests/Runtime/Syncing/SyncDirectionFromServerHostToOwner.cs index 50c98e9e593..2483e4f4b80 100644 --- a/Assets/Tests/Runtime/Syncing/SyncDirectionFromServerHostToOwner.cs +++ b/Assets/Tests/Runtime/Syncing/SyncDirectionFromServerHostToOwner.cs @@ -10,17 +10,17 @@ namespace Mirage.Tests.Runtime.Syncing public class MockPlayerHook : NetworkBehaviour { [SyncVar(hook = nameof(MyNumberEventChanged), invokeHookOnServer = true)] - public int myNumberEvent; + public int myNumberEvent { get; set; } [SyncVar(hook = nameof(MyNumberEventChanged), invokeHookOnServer = false)] - public int myNumberEvent2; + public int myNumberEvent2 { get; set; } public event Action MyNumberEventChanged; [SyncVar(hook = nameof(MyNumberMethodChanged), invokeHookOnServer = true)] - public int myNumberMethod; + public int myNumberMethod { get; set; } public event Action MyNumberMethodChangedCalled; public void MyNumberMethodChanged(int newValue) diff --git a/Assets/Tests/Runtime/Syncing/SyncVarHookOrderTest.cs b/Assets/Tests/Runtime/Syncing/SyncVarHookOrderTest.cs index 359aa9578dd..9aafd9c24ce 100644 --- a/Assets/Tests/Runtime/Syncing/SyncVarHookOrderTest.cs +++ b/Assets/Tests/Runtime/Syncing/SyncVarHookOrderTest.cs @@ -12,9 +12,9 @@ namespace Mirage.Tests.Runtime.Syncing public class HookOrderBehaviour : NetworkBehaviour { [SyncVar(hook = nameof(OnValue1Changed))] - public int value1; + public int value1 { get; set; } [SyncVar(hook = nameof(OnValue2Changed))] - public int value2; + public int value2 { get; set; } public Action HookCalled1; public Action HookCalled2; diff --git a/Assets/Tests/Runtime/Syncing/SyncVarInitialStateFlagTest.cs b/Assets/Tests/Runtime/Syncing/SyncVarInitialStateFlagTest.cs index d16d911ae4e..368bf52fce8 100644 --- a/Assets/Tests/Runtime/Syncing/SyncVarInitialStateFlagTest.cs +++ b/Assets/Tests/Runtime/Syncing/SyncVarInitialStateFlagTest.cs @@ -10,7 +10,7 @@ public class SyncVarInitialStateFlagBehaviour : NetworkBehaviour public event Action Hook; [SyncVar(hook = nameof(Hook))] - public int number; + public int number { get; set; } } public class SyncVarInitialStateFlagTest : ClientServerSetup { diff --git a/Assets/Tests/Runtime/Syncing/SyncVarVirtualTest.cs b/Assets/Tests/Runtime/Syncing/SyncVarVirtualTest.cs index acf3641636d..8fbf7d6e359 100644 --- a/Assets/Tests/Runtime/Syncing/SyncVarVirtualTest.cs +++ b/Assets/Tests/Runtime/Syncing/SyncVarVirtualTest.cs @@ -8,9 +8,9 @@ namespace Mirage.Tests.Runtime.Syncing public abstract class SyncVarHookTesterBase : NetworkBehaviour { [SyncVar(hook = nameof(OnValue1Changed))] - public float value1; + public float value1 { get; set; } [SyncVar(hook = nameof(OnValue2Changed))] - public float value2; + public float value2 { get; set; } public event Action OnValue2ChangedVirtualCalled; From 41128ccff1c86018e97d952739974ac432a59954 Mon Sep 17 00:00:00 2001 From: James Frowen Date: Sat, 30 May 2026 23:57:13 +0100 Subject: [PATCH 08/41] running test generator --- .../Generated/BitCountFromRangeTests.meta | 8 + .../BitCountFromRange_MyByteEnum_0_3.cs | 174 +++++++++++++++ .../BitCountFromRange_MyByteEnum_0_3.cs.meta | 11 + .../BitCountFromRange_MyDirection_N1_1.cs | 173 +++++++++++++++ ...BitCountFromRange_MyDirection_N1_1.cs.meta | 11 + .../BitCountFromRange_int_N1000_0.cs | 167 ++++++++++++++ .../BitCountFromRange_int_N1000_0.cs.meta | 11 + .../BitCountFromRange_int_N10_10.cs | 167 ++++++++++++++ .../BitCountFromRange_int_N10_10.cs.meta | 11 + .../BitCountFromRange_int_N20000_20000.cs | 167 ++++++++++++++ ...BitCountFromRange_int_N20000_20000.cs.meta | 11 + .../BitCountFromRange_int_N2000_N1000.cs | 167 ++++++++++++++ .../BitCountFromRange_int_N2000_N1000.cs.meta | 11 + ...untFromRange_int_N2147483648_2147483647.cs | 167 ++++++++++++++ ...omRange_int_N2147483648_2147483647.cs.meta | 11 + ...FromRange_int_N2147483648_2147483647max.cs | 167 ++++++++++++++ ...ange_int_N2147483648_2147483647max.cs.meta | 11 + ...FromRange_int_N2147483648_2147483647min.cs | 167 ++++++++++++++ ...ange_int_N2147483648_2147483647min.cs.meta | 11 + .../BitCountFromRange_short_N1000_1000.cs | 167 ++++++++++++++ ...BitCountFromRange_short_N1000_1000.cs.meta | 11 + .../BitCountFromRange_short_N10_10.cs | 167 ++++++++++++++ .../BitCountFromRange_short_N10_10.cs.meta | 11 + .../BitCountFromRange_short_N32768_32767.cs | 167 ++++++++++++++ ...tCountFromRange_short_N32768_32767.cs.meta | 11 + .../BitCountFromRange_uint_0_5000.cs | 167 ++++++++++++++ .../BitCountFromRange_uint_0_5000.cs.meta | 11 + .../BitCountFromRange_ushort_0_65535.cs | 167 ++++++++++++++ .../BitCountFromRange_ushort_0_65535.cs.meta | 11 + Assets/Tests/Generated/BitCountTests.meta | 8 + .../BitCountBehaviour_MyByteEnum_4.cs | 175 +++++++++++++++ .../BitCountBehaviour_MyByteEnum_4.cs.meta | 11 + .../BitCountBehaviour_MyEnum_4.cs | 175 +++++++++++++++ .../BitCountBehaviour_MyEnum_4.cs.meta | 11 + .../BitCountTests/BitCountBehaviour_int_10.cs | 167 ++++++++++++++ .../BitCountBehaviour_int_10.cs.meta | 11 + .../BitCountTests/BitCountBehaviour_int_17.cs | 167 ++++++++++++++ .../BitCountBehaviour_int_17.cs.meta | 11 + .../BitCountTests/BitCountBehaviour_int_32.cs | 167 ++++++++++++++ .../BitCountBehaviour_int_32.cs.meta | 11 + .../BitCountBehaviour_short_12.cs | 167 ++++++++++++++ .../BitCountBehaviour_short_12.cs.meta | 11 + .../BitCountBehaviour_short_4.cs | 167 ++++++++++++++ .../BitCountBehaviour_short_4.cs.meta | 11 + .../BitCountBehaviour_ulong_24.cs | 167 ++++++++++++++ .../BitCountBehaviour_ulong_24.cs.meta | 11 + .../BitCountBehaviour_ulong_5.cs | 167 ++++++++++++++ .../BitCountBehaviour_ulong_5.cs.meta | 11 + .../BitCountBehaviour_ulong_64.cs | 167 ++++++++++++++ .../BitCountBehaviour_ulong_64.cs.meta | 11 + Assets/Tests/Generated/FloatPackTests.meta | 8 + .../FloatPackBehaviour_100_10.cs | 167 ++++++++++++++ .../FloatPackBehaviour_100_10.cs.meta | 11 + .../FloatPackBehaviour_100_14.cs | 167 ++++++++++++++ .../FloatPackBehaviour_100_14.cs.meta | 11 + .../FloatPackBehaviour_10_10.cs | 167 ++++++++++++++ .../FloatPackBehaviour_10_10.cs.meta | 11 + .../FloatPackTests/FloatPackBehaviour_1_8.cs | 167 ++++++++++++++ .../FloatPackBehaviour_1_8.cs.meta | 11 + .../FloatPackBehaviour_500_14.cs | 167 ++++++++++++++ .../FloatPackBehaviour_500_14.cs.meta | 11 + .../FloatPackBehaviour_500_17.cs | 167 ++++++++++++++ .../FloatPackBehaviour_500_17.cs.meta | 11 + .../Tests/Generated/QuaternionPackTests.meta | 8 + .../QuaternionPackBehaviour_10_0.cs | 178 +++++++++++++++ .../QuaternionPackBehaviour_10_0.cs.meta | 11 + .../QuaternionPackBehaviour_10_45.cs | 178 +++++++++++++++ .../QuaternionPackBehaviour_10_45.cs.meta | 11 + .../QuaternionPackBehaviour_8_0.cs | 178 +++++++++++++++ .../QuaternionPackBehaviour_8_0.cs.meta | 11 + .../QuaternionPackBehaviour_8_45.cs | 178 +++++++++++++++ .../QuaternionPackBehaviour_8_45.cs.meta | 11 + .../QuaternionPackBehaviour_9_0.cs | 178 +++++++++++++++ .../QuaternionPackBehaviour_9_0.cs.meta | 11 + .../QuaternionPackBehaviour_9_45.cs | 178 +++++++++++++++ .../QuaternionPackBehaviour_9_45.cs.meta | 11 + Assets/Tests/Generated/VarIntBlocksTests.meta | 8 + .../VarIntBlocksBehaviour_MyEnumByte_4.cs | 203 ++++++++++++++++++ ...VarIntBlocksBehaviour_MyEnumByte_4.cs.meta | 11 + .../VarIntBlocksBehaviour_MyEnum_4.cs | 203 ++++++++++++++++++ .../VarIntBlocksBehaviour_MyEnum_4.cs.meta | 11 + .../VarIntBlocksBehaviour_int_6.cs | 192 +++++++++++++++++ .../VarIntBlocksBehaviour_int_6.cs.meta | 11 + .../VarIntBlocksBehaviour_int_7.cs | 191 ++++++++++++++++ .../VarIntBlocksBehaviour_int_7.cs.meta | 11 + .../VarIntBlocksBehaviour_long_8.cs | 192 +++++++++++++++++ .../VarIntBlocksBehaviour_long_8.cs.meta | 11 + .../VarIntBlocksBehaviour_short_6.cs | 192 +++++++++++++++++ .../VarIntBlocksBehaviour_short_6.cs.meta | 11 + .../VarIntBlocksBehaviour_uint_6.cs | 192 +++++++++++++++++ .../VarIntBlocksBehaviour_uint_6.cs.meta | 11 + .../VarIntBlocksBehaviour_uint_7.cs | 192 +++++++++++++++++ .../VarIntBlocksBehaviour_uint_7.cs.meta | 11 + .../VarIntBlocksBehaviour_uint_8.cs | 193 +++++++++++++++++ .../VarIntBlocksBehaviour_uint_8.cs.meta | 11 + .../VarIntBlocksBehaviour_ulong_9.cs | 192 +++++++++++++++++ .../VarIntBlocksBehaviour_ulong_9.cs.meta | 11 + .../VarIntBlocksBehaviour_ushort_7.cs | 192 +++++++++++++++++ .../VarIntBlocksBehaviour_ushort_7.cs.meta | 11 + Assets/Tests/Generated/VarIntTests.meta | 8 + .../VarIntBehaviour_MyEnumByte_4_64.cs | 203 ++++++++++++++++++ .../VarIntBehaviour_MyEnumByte_4_64.cs.meta | 11 + .../VarIntBehaviour_MyEnum_4_64.cs | 203 ++++++++++++++++++ .../VarIntBehaviour_MyEnum_4_64.cs.meta | 11 + .../VarIntBehaviour_int_100_1000.cs | 192 +++++++++++++++++ .../VarIntBehaviour_int_100_1000.cs.meta | 11 + .../VarIntBehaviour_int_100_10000.cs | 191 ++++++++++++++++ .../VarIntBehaviour_int_100_10000.cs.meta | 11 + .../VarIntBehaviour_long_100_1000.cs | 192 +++++++++++++++++ .../VarIntBehaviour_long_100_1000.cs.meta | 11 + .../VarIntBehaviour_short_100_1000.cs | 192 +++++++++++++++++ .../VarIntBehaviour_short_100_1000.cs.meta | 11 + .../VarIntBehaviour_uint_100_1000.cs | 192 +++++++++++++++++ .../VarIntBehaviour_uint_100_1000.cs.meta | 11 + .../VarIntBehaviour_uint_255_64000.cs | 192 +++++++++++++++++ .../VarIntBehaviour_uint_255_64000.cs.meta | 11 + .../VarIntBehaviour_uint_500_32000.cs | 193 +++++++++++++++++ .../VarIntBehaviour_uint_500_32000.cs.meta | 11 + .../VarIntBehaviour_ulong_100_1000.cs | 192 +++++++++++++++++ .../VarIntBehaviour_ulong_100_1000.cs.meta | 11 + .../VarIntBehaviour_ushort_100_1000.cs | 192 +++++++++++++++++ .../VarIntBehaviour_ushort_100_1000.cs.meta | 11 + Assets/Tests/Generated/Vector2PackTests.meta | 8 + .../Vector2PackBehaviour_1000_27b2.cs | 173 +++++++++++++++ .../Vector2PackBehaviour_1000_27b2.cs.meta | 11 + .../Vector2PackBehaviour_1000_27f.cs | 173 +++++++++++++++ .../Vector2PackBehaviour_1000_27f.cs.meta | 11 + .../Vector2PackBehaviour_1000_27f2.cs | 173 +++++++++++++++ .../Vector2PackBehaviour_1000_27f2.cs.meta | 11 + .../Vector2PackBehaviour_1000_30b.cs | 173 +++++++++++++++ .../Vector2PackBehaviour_1000_30b.cs.meta | 11 + .../Vector2PackBehaviour_100_18b2.cs | 173 +++++++++++++++ .../Vector2PackBehaviour_100_18b2.cs.meta | 11 + .../Vector2PackBehaviour_100_18f.cs | 173 +++++++++++++++ .../Vector2PackBehaviour_100_18f.cs.meta | 11 + .../Vector2PackBehaviour_100_18f2.cs | 173 +++++++++++++++ .../Vector2PackBehaviour_100_18f2.cs.meta | 11 + .../Vector2PackBehaviour_100_20b.cs | 173 +++++++++++++++ .../Vector2PackBehaviour_100_20b.cs.meta | 11 + .../Vector2PackBehaviour_200_26b.cs | 173 +++++++++++++++ .../Vector2PackBehaviour_200_26b.cs.meta | 11 + .../Vector2PackBehaviour_200_26b2.cs | 173 +++++++++++++++ .../Vector2PackBehaviour_200_26b2.cs.meta | 11 + .../Vector2PackBehaviour_200_26f.cs | 173 +++++++++++++++ .../Vector2PackBehaviour_200_26f.cs.meta | 11 + .../Vector2PackBehaviour_200_26f2.cs | 173 +++++++++++++++ .../Vector2PackBehaviour_200_26f2.cs.meta | 11 + Assets/Tests/Generated/Vector3PackTests.meta | 8 + .../Vector3PackBehaviour_1000_42b3.cs | 176 +++++++++++++++ .../Vector3PackBehaviour_1000_42b3.cs.meta | 11 + .../Vector3PackBehaviour_1000_42f.cs | 176 +++++++++++++++ .../Vector3PackBehaviour_1000_42f.cs.meta | 11 + .../Vector3PackBehaviour_1000_42f3.cs | 176 +++++++++++++++ .../Vector3PackBehaviour_1000_42f3.cs.meta | 11 + .../Vector3PackBehaviour_1000_45b.cs | 176 +++++++++++++++ .../Vector3PackBehaviour_1000_45b.cs.meta | 11 + .../Vector3PackBehaviour_100_28b3.cs | 176 +++++++++++++++ .../Vector3PackBehaviour_100_28b3.cs.meta | 11 + .../Vector3PackBehaviour_100_28f.cs | 176 +++++++++++++++ .../Vector3PackBehaviour_100_28f.cs.meta | 11 + .../Vector3PackBehaviour_100_28f3.cs | 176 +++++++++++++++ .../Vector3PackBehaviour_100_28f3.cs.meta | 11 + .../Vector3PackBehaviour_100_30b.cs | 176 +++++++++++++++ .../Vector3PackBehaviour_100_30b.cs.meta | 11 + .../Vector3PackBehaviour_200_39b.cs | 176 +++++++++++++++ .../Vector3PackBehaviour_200_39b.cs.meta | 11 + .../Vector3PackBehaviour_200_39b3.cs | 176 +++++++++++++++ .../Vector3PackBehaviour_200_39b3.cs.meta | 11 + .../Vector3PackBehaviour_200_39f.cs | 176 +++++++++++++++ .../Vector3PackBehaviour_200_39f.cs.meta | 11 + .../Vector3PackBehaviour_200_39f3.cs | 176 +++++++++++++++ .../Vector3PackBehaviour_200_39f3.cs.meta | 11 + Assets/Tests/Generated/ZigZagTests.meta | 8 + .../ZigZagBehaviour_MyEnum2_4_negative.cs | 173 +++++++++++++++ ...ZigZagBehaviour_MyEnum2_4_negative.cs.meta | 11 + .../ZigZagTests/ZigZagBehaviour_MyEnum_4.cs | 173 +++++++++++++++ .../ZigZagBehaviour_MyEnum_4.cs.meta | 11 + .../ZigZagTests/ZigZagBehaviour_int_10.cs | 167 ++++++++++++++ .../ZigZagBehaviour_int_10.cs.meta | 11 + .../ZigZagBehaviour_int_10_negative.cs | 167 ++++++++++++++ .../ZigZagBehaviour_int_10_negative.cs.meta | 11 + .../ZigZagTests/ZigZagBehaviour_long_10.cs | 167 ++++++++++++++ .../ZigZagBehaviour_long_10.cs.meta | 11 + .../ZigZagBehaviour_long_10_negative.cs | 167 ++++++++++++++ .../ZigZagBehaviour_long_10_negative.cs.meta | 11 + .../ZigZagTests/ZigZagBehaviour_short_10.cs | 167 ++++++++++++++ .../ZigZagBehaviour_short_10.cs.meta | 11 + .../ZigZagBehaviour_short_10_negative.cs | 167 ++++++++++++++ .../ZigZagBehaviour_short_10_negative.cs.meta | 11 + 189 files changed, 16973 insertions(+) create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests.meta create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyByteEnum_0_3.cs create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyByteEnum_0_3.cs.meta create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyDirection_N1_1.cs create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyDirection_N1_1.cs.meta create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N1000_0.cs create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N1000_0.cs.meta create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N10_10.cs create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N10_10.cs.meta create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N20000_20000.cs create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N20000_20000.cs.meta create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2000_N1000.cs create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2000_N1000.cs.meta create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647.cs create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647.cs.meta create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647max.cs create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647max.cs.meta create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647min.cs create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647min.cs.meta create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N1000_1000.cs create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N1000_1000.cs.meta create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N10_10.cs create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N10_10.cs.meta create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N32768_32767.cs create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N32768_32767.cs.meta create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_uint_0_5000.cs create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_uint_0_5000.cs.meta create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_ushort_0_65535.cs create mode 100644 Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_ushort_0_65535.cs.meta create mode 100644 Assets/Tests/Generated/BitCountTests.meta create mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyByteEnum_4.cs create mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyByteEnum_4.cs.meta create mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyEnum_4.cs create mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyEnum_4.cs.meta create mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_10.cs create mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_10.cs.meta create mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_17.cs create mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_17.cs.meta create mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_32.cs create mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_32.cs.meta create mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_12.cs create mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_12.cs.meta create mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_4.cs create mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_4.cs.meta create mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_24.cs create mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_24.cs.meta create mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_5.cs create mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_5.cs.meta create mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_64.cs create mode 100644 Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_64.cs.meta create mode 100644 Assets/Tests/Generated/FloatPackTests.meta create mode 100644 Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_10.cs create mode 100644 Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_10.cs.meta create mode 100644 Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_14.cs create mode 100644 Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_14.cs.meta create mode 100644 Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_10_10.cs create mode 100644 Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_10_10.cs.meta create mode 100644 Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_1_8.cs create mode 100644 Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_1_8.cs.meta create mode 100644 Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_14.cs create mode 100644 Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_14.cs.meta create mode 100644 Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_17.cs create mode 100644 Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_17.cs.meta create mode 100644 Assets/Tests/Generated/QuaternionPackTests.meta create mode 100644 Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_0.cs create mode 100644 Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_0.cs.meta create mode 100644 Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_45.cs create mode 100644 Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_45.cs.meta create mode 100644 Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_0.cs create mode 100644 Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_0.cs.meta create mode 100644 Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_45.cs create mode 100644 Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_45.cs.meta create mode 100644 Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_0.cs create mode 100644 Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_0.cs.meta create mode 100644 Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_45.cs create mode 100644 Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_45.cs.meta create mode 100644 Assets/Tests/Generated/VarIntBlocksTests.meta create mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnumByte_4.cs create mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnumByte_4.cs.meta create mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnum_4.cs create mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnum_4.cs.meta create mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_6.cs create mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_6.cs.meta create mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_7.cs create mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_7.cs.meta create mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_long_8.cs create mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_long_8.cs.meta create mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_short_6.cs create mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_short_6.cs.meta create mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_6.cs create mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_6.cs.meta create mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_7.cs create mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_7.cs.meta create mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_8.cs create mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_8.cs.meta create mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ulong_9.cs create mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ulong_9.cs.meta create mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ushort_7.cs create mode 100644 Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ushort_7.cs.meta create mode 100644 Assets/Tests/Generated/VarIntTests.meta create mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnumByte_4_64.cs create mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnumByte_4_64.cs.meta create mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnum_4_64.cs create mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnum_4_64.cs.meta create mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_1000.cs create mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_1000.cs.meta create mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_10000.cs create mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_10000.cs.meta create mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_long_100_1000.cs create mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_long_100_1000.cs.meta create mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_short_100_1000.cs create mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_short_100_1000.cs.meta create mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_100_1000.cs create mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_100_1000.cs.meta create mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_255_64000.cs create mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_255_64000.cs.meta create mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_500_32000.cs create mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_500_32000.cs.meta create mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ulong_100_1000.cs create mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ulong_100_1000.cs.meta create mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ushort_100_1000.cs create mode 100644 Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ushort_100_1000.cs.meta create mode 100644 Assets/Tests/Generated/Vector2PackTests.meta create mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27b2.cs create mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27b2.cs.meta create mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f.cs create mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f.cs.meta create mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f2.cs create mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f2.cs.meta create mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_30b.cs create mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_30b.cs.meta create mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18b2.cs create mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18b2.cs.meta create mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f.cs create mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f.cs.meta create mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f2.cs create mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f2.cs.meta create mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_20b.cs create mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_20b.cs.meta create mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b.cs create mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b.cs.meta create mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b2.cs create mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b2.cs.meta create mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f.cs create mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f.cs.meta create mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f2.cs create mode 100644 Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f2.cs.meta create mode 100644 Assets/Tests/Generated/Vector3PackTests.meta create mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42b3.cs create mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42b3.cs.meta create mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f.cs create mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f.cs.meta create mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f3.cs create mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f3.cs.meta create mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_45b.cs create mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_45b.cs.meta create mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28b3.cs create mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28b3.cs.meta create mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f.cs create mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f.cs.meta create mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f3.cs create mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f3.cs.meta create mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_30b.cs create mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_30b.cs.meta create mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b.cs create mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b.cs.meta create mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b3.cs create mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b3.cs.meta create mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f.cs create mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f.cs.meta create mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f3.cs create mode 100644 Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f3.cs.meta create mode 100644 Assets/Tests/Generated/ZigZagTests.meta create mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum2_4_negative.cs create mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum2_4_negative.cs.meta create mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum_4.cs create mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum_4.cs.meta create mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10.cs create mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10.cs.meta create mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10_negative.cs create mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10_negative.cs.meta create mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10.cs create mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10.cs.meta create mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10_negative.cs create mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10_negative.cs.meta create mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10.cs create mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10.cs.meta create mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10_negative.cs create mode 100644 Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10_negative.cs.meta diff --git a/Assets/Tests/Generated/BitCountFromRangeTests.meta b/Assets/Tests/Generated/BitCountFromRangeTests.meta new file mode 100644 index 00000000000..1992c1d0462 --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8cb64a68508b9124e83b82d7b32e82c9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyByteEnum_0_3.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyByteEnum_0_3.cs new file mode 100644 index 00000000000..90f63f82da5 --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyByteEnum_0_3.cs @@ -0,0 +1,174 @@ +// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.MyByteEnum_0_3 +{ + [System.Serializable] + public enum MyByteEnum : byte + { + None = 0, + Slow = 1, + Fast = 2, + ReallyFast = 3, + } + public class BitPackBehaviour : NetworkBehaviour + { + [BitCountFromRange(0, 3)] + [SyncVar] public MyByteEnum MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCountFromRange(0, 3)] MyByteEnum myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCountFromRange(0, 3)] + public MyByteEnum MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCountFromRange(0, 3)] + public MyByteEnum MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const MyByteEnum value = (MyByteEnum)3; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(2)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(2)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (2 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"2 bits is 1 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((2 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"2 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(2)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(2)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyByteEnum_0_3.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyByteEnum_0_3.cs.meta new file mode 100644 index 00000000000..b95cf818a93 --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyByteEnum_0_3.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f0ea6f23680a449479ca061794962e59 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyDirection_N1_1.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyDirection_N1_1.cs new file mode 100644 index 00000000000..c7e2c0835a8 --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyDirection_N1_1.cs @@ -0,0 +1,173 @@ +// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.MyDirection_N1_1 +{ + [System.Serializable] + public enum MyDirection + { + Left = -1, + None = 0, + Right = 1, + } + public class BitPackBehaviour : NetworkBehaviour + { + [BitCountFromRange(-1, 1)] + [SyncVar] public MyDirection MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCountFromRange(-1, 1)] MyDirection myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCountFromRange(-1, 1)] + public MyDirection MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCountFromRange(-1, 1)] + public MyDirection MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const MyDirection value = (MyDirection)1; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(2)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(2)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (2 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"2 bits is 1 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((2 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"2 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(2)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(2)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyDirection_N1_1.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyDirection_N1_1.cs.meta new file mode 100644 index 00000000000..f69e69994b7 --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_MyDirection_N1_1.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2654c2521316ddd4a98e17939c9cdcd8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N1000_0.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N1000_0.cs new file mode 100644 index 00000000000..13057f96489 --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N1000_0.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.int_N1000_0 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [BitCountFromRange(-1000, 0)] + [SyncVar] public int MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCountFromRange(-1000, 0)] int myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCountFromRange(-1000, 0)] + public int MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCountFromRange(-1000, 0)] + public int MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const int value = -3; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(10)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(10)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (10 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is 2 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((10 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(10)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(10)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N1000_0.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N1000_0.cs.meta new file mode 100644 index 00000000000..d00037efb26 --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N1000_0.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e7737807c8d046b439d43617646710d8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N10_10.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N10_10.cs new file mode 100644 index 00000000000..95dbca44687 --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N10_10.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.int_N10_10 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [BitCountFromRange(-10, 10)] + [SyncVar] public int MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCountFromRange(-10, 10)] int myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCountFromRange(-10, 10)] + public int MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCountFromRange(-10, 10)] + public int MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const int value = -3; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(5)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(5)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (5 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"5 bits is 1 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((5 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"5 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(5)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(5)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N10_10.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N10_10.cs.meta new file mode 100644 index 00000000000..04c8e5e9cc5 --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N10_10.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 59d7a4e915271844488f13295fc7a8f3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N20000_20000.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N20000_20000.cs new file mode 100644 index 00000000000..49ed5238cf5 --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N20000_20000.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.int_N20000_20000 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [BitCountFromRange(-20000, 20000)] + [SyncVar] public int MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCountFromRange(-20000, 20000)] int myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCountFromRange(-20000, 20000)] + public int MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCountFromRange(-20000, 20000)] + public int MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const int value = 20; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(16)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(16)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (16 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"16 bits is 2 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((16 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"16 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(16)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(16)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N20000_20000.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N20000_20000.cs.meta new file mode 100644 index 00000000000..ea2663d1130 --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N20000_20000.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 24db0dadb6cdcff4c86a6b891b96c4f4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2000_N1000.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2000_N1000.cs new file mode 100644 index 00000000000..780bf8efaf6 --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2000_N1000.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.int_N2000_N1000 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [BitCountFromRange(-2000, -1000)] + [SyncVar] public int MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCountFromRange(-2000, -1000)] int myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCountFromRange(-2000, -1000)] + public int MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCountFromRange(-2000, -1000)] + public int MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const int value = -1400; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(10)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(10)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (10 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is 2 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((10 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(10)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(10)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2000_N1000.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2000_N1000.cs.meta new file mode 100644 index 00000000000..5a5bfee54cf --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2000_N1000.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c5a1e350102a4064792c236943e12b32 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647.cs new file mode 100644 index 00000000000..09e99506155 --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.int_N2147483648_2147483647 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [BitCountFromRange(-2147483648, 2147483647)] + [SyncVar] public int MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCountFromRange(-2147483648, 2147483647)] int myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCountFromRange(-2147483648, 2147483647)] + public int MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCountFromRange(-2147483648, 2147483647)] + public int MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const int value = 20; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(32)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(32)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (32 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"32 bits is 4 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((32 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"32 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(32)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(32)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647.cs.meta new file mode 100644 index 00000000000..c078b8e8650 --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5890a0f5c296a564fb2ab28ba115a4df +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647max.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647max.cs new file mode 100644 index 00000000000..8323a795b38 --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647max.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.int_N2147483648_2147483647max +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [BitCountFromRange(-2147483648, 2147483647)] + [SyncVar] public int MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCountFromRange(-2147483648, 2147483647)] int myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCountFromRange(-2147483648, 2147483647)] + public int MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCountFromRange(-2147483648, 2147483647)] + public int MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const int value = 2147483647; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(32)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(32)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (32 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"32 bits is 4 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((32 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"32 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(32)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(32)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647max.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647max.cs.meta new file mode 100644 index 00000000000..9cb26d9a380 --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647max.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 23989d597941a43468cdbe44a0462e5f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647min.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647min.cs new file mode 100644 index 00000000000..3403d94050a --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647min.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.int_N2147483648_2147483647min +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [BitCountFromRange(-2147483648, 2147483647)] + [SyncVar] public int MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCountFromRange(-2147483648, 2147483647)] int myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCountFromRange(-2147483648, 2147483647)] + public int MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCountFromRange(-2147483648, 2147483647)] + public int MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const int value = -2147483648; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(32)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(32)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (32 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"32 bits is 4 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((32 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"32 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(32)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(32)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647min.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647min.cs.meta new file mode 100644 index 00000000000..810792e817a --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_int_N2147483648_2147483647min.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fbc3722634dcb374eb7fade5be726c0b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N1000_1000.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N1000_1000.cs new file mode 100644 index 00000000000..95793ea8076 --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N1000_1000.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.short_N1000_1000 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [BitCountFromRange(-1000, 1000)] + [SyncVar] public short MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCountFromRange(-1000, 1000)] short myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCountFromRange(-1000, 1000)] + public short MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCountFromRange(-1000, 1000)] + public short MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const short value = 20; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(11)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(11)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (11 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"11 bits is 2 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((11 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"11 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(11)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(11)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N1000_1000.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N1000_1000.cs.meta new file mode 100644 index 00000000000..11abfc7d15f --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N1000_1000.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 043ee658c5663f941b9bdbe629203904 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N10_10.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N10_10.cs new file mode 100644 index 00000000000..d77aa30b9ed --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N10_10.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.short_N10_10 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [BitCountFromRange(-10, 10)] + [SyncVar] public short MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCountFromRange(-10, 10)] short myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCountFromRange(-10, 10)] + public short MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCountFromRange(-10, 10)] + public short MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const short value = 3; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(5)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(5)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (5 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"5 bits is 1 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((5 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"5 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(5)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(5)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N10_10.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N10_10.cs.meta new file mode 100644 index 00000000000..99897962e41 --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N10_10.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6db00fed3a6c6db479014d4fb0da8bfa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N32768_32767.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N32768_32767.cs new file mode 100644 index 00000000000..a88d69ff713 --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N32768_32767.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.short_N32768_32767 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [BitCountFromRange(-32768, 32767)] + [SyncVar] public short MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCountFromRange(-32768, 32767)] short myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCountFromRange(-32768, 32767)] + public short MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCountFromRange(-32768, 32767)] + public short MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const short value = 20; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(16)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(16)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (16 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"16 bits is 2 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((16 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"16 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(16)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(16)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N32768_32767.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N32768_32767.cs.meta new file mode 100644 index 00000000000..ab02b553d93 --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_short_N32768_32767.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b199c8868e0116a4f80bbac71aa09a44 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_uint_0_5000.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_uint_0_5000.cs new file mode 100644 index 00000000000..fac3b910b5f --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_uint_0_5000.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.uint_0_5000 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [BitCountFromRange(0, 5000)] + [SyncVar] public uint MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCountFromRange(0, 5000)] uint myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCountFromRange(0, 5000)] + public uint MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCountFromRange(0, 5000)] + public uint MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const uint value = 20; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(13)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(13)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (13 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"13 bits is 2 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((13 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"13 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(13)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(13)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_uint_0_5000.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_uint_0_5000.cs.meta new file mode 100644 index 00000000000..f48241b4b15 --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_uint_0_5000.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fc1514bc9e72a8b4a95552437de454a2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_ushort_0_65535.cs b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_ushort_0_65535.cs new file mode 100644 index 00000000000..5350167493d --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_ushort_0_65535.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY BitCountFromRangeTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.BitCountFromRangeAttributeTests.ushort_0_65535 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [BitCountFromRange(0, 65535)] + [SyncVar] public ushort MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCountFromRange(0, 65535)] ushort myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCountFromRange(0, 65535)] + public ushort MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCountFromRange(0, 65535)] + public ushort MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const ushort value = 20; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(16)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(16)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (16 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"16 bits is 2 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((16 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"16 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(16)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(16)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_ushort_0_65535.cs.meta b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_ushort_0_65535.cs.meta new file mode 100644 index 00000000000..fc90c8300cf --- /dev/null +++ b/Assets/Tests/Generated/BitCountFromRangeTests/BitCountFromRange_ushort_0_65535.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ac04d5e2e5d065d479de23438930b877 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountTests.meta b/Assets/Tests/Generated/BitCountTests.meta new file mode 100644 index 00000000000..706e5c13295 --- /dev/null +++ b/Assets/Tests/Generated/BitCountTests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d467a8a2dda82ad4a8133a09f6ffac51 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyByteEnum_4.cs b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyByteEnum_4.cs new file mode 100644 index 00000000000..6e36584c109 --- /dev/null +++ b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyByteEnum_4.cs @@ -0,0 +1,175 @@ +// DO NOT EDIT: GENERATED BY BitCountTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.BitCountAttributeTests.MyByteEnum_4 +{ + [System.Flags, System.Serializable] + public enum MyByteEnum : byte + { + None = 0, + HasHealth = 1, + HasArmor = 2, + HasGun = 4, + HasAmmo = 8, + } + public class BitPackBehaviour : NetworkBehaviour + { + [BitCount(4)] + [SyncVar] public MyByteEnum MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCount(4)] MyByteEnum myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCount(4)] + public MyByteEnum MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCount(4)] + public MyByteEnum MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const MyByteEnum value = (MyByteEnum)3; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(4)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(4)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (4 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"4 bits is 1 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((4 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"4 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(4)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(4)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyByteEnum_4.cs.meta b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyByteEnum_4.cs.meta new file mode 100644 index 00000000000..d8b85647df5 --- /dev/null +++ b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyByteEnum_4.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 07a82e15515c61743a462a884a5e0c58 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyEnum_4.cs b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyEnum_4.cs new file mode 100644 index 00000000000..7c2e4c4c33e --- /dev/null +++ b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyEnum_4.cs @@ -0,0 +1,175 @@ +// DO NOT EDIT: GENERATED BY BitCountTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.BitCountAttributeTests.MyEnum_4 +{ + [System.Flags, System.Serializable] + public enum MyEnum + { + None = 0, + HasHealth = 1, + HasArmor = 2, + HasGun = 4, + HasAmmo = 8, + } + public class BitPackBehaviour : NetworkBehaviour + { + [BitCount(4)] + [SyncVar] public MyEnum MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCount(4)] MyEnum myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCount(4)] + public MyEnum MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCount(4)] + public MyEnum MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const MyEnum value = (MyEnum)3; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(4)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(4)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (4 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"4 bits is 1 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((4 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"4 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(4)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(4)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyEnum_4.cs.meta b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyEnum_4.cs.meta new file mode 100644 index 00000000000..417ba6194ea --- /dev/null +++ b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_MyEnum_4.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a88893ffaef084b45acf2820fb664975 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_10.cs b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_10.cs new file mode 100644 index 00000000000..b6a2692b269 --- /dev/null +++ b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_10.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY BitCountTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.BitCountAttributeTests.int_10 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [BitCount(10)] + [SyncVar] public int MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCount(10)] int myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCount(10)] + public int MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCount(10)] + public int MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const int value = 20; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(10)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(10)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (10 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is 2 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((10 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(10)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(10)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_10.cs.meta b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_10.cs.meta new file mode 100644 index 00000000000..468cbc7af03 --- /dev/null +++ b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_10.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 35eb226fb46a6194a948d714de75dc54 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_17.cs b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_17.cs new file mode 100644 index 00000000000..01d921818ec --- /dev/null +++ b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_17.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY BitCountTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.BitCountAttributeTests.int_17 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [BitCount(17)] + [SyncVar] public int MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCount(17)] int myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCount(17)] + public int MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCount(17)] + public int MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const int value = 20; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(17)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(17)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (17 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"17 bits is 3 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((17 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"17 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(17)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(17)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_17.cs.meta b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_17.cs.meta new file mode 100644 index 00000000000..96a2c3ba411 --- /dev/null +++ b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_17.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c88ea0247a9d41043b6247338c9eca4d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_32.cs b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_32.cs new file mode 100644 index 00000000000..b5763d61bd1 --- /dev/null +++ b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_32.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY BitCountTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.BitCountAttributeTests.int_32 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [BitCount(32)] + [SyncVar] public int MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCount(32)] int myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCount(32)] + public int MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCount(32)] + public int MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const int value = 20; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(32)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(32)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (32 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"32 bits is 4 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((32 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"32 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(32)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(32)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_32.cs.meta b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_32.cs.meta new file mode 100644 index 00000000000..ea77a629c26 --- /dev/null +++ b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_int_32.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6e3bb7a2ea9da0846a7ce769f31e1b3c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_12.cs b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_12.cs new file mode 100644 index 00000000000..87e1b39a2f2 --- /dev/null +++ b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_12.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY BitCountTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.BitCountAttributeTests.short_12 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [BitCount(12)] + [SyncVar] public short MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCount(12)] short myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCount(12)] + public short MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCount(12)] + public short MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const short value = 20; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(12)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(12)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (12 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"12 bits is 2 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((12 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"12 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(12)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(12)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_12.cs.meta b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_12.cs.meta new file mode 100644 index 00000000000..fc4fd397a86 --- /dev/null +++ b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_12.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 445320b56bb07754cb55b25514abf4d4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_4.cs b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_4.cs new file mode 100644 index 00000000000..ae7fe72dd3e --- /dev/null +++ b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_4.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY BitCountTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.BitCountAttributeTests.short_4 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [BitCount(4)] + [SyncVar] public short MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCount(4)] short myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCount(4)] + public short MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCount(4)] + public short MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const short value = 3; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(4)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(4)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (4 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"4 bits is 1 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((4 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"4 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(4)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(4)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_4.cs.meta b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_4.cs.meta new file mode 100644 index 00000000000..e2594abe8a6 --- /dev/null +++ b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_short_4.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 66b0359ad03bbbd438468152ea395144 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_24.cs b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_24.cs new file mode 100644 index 00000000000..9763e1f0119 --- /dev/null +++ b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_24.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY BitCountTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.BitCountAttributeTests.ulong_24 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [BitCount(24)] + [SyncVar] public ulong MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCount(24)] ulong myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCount(24)] + public ulong MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCount(24)] + public ulong MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const ulong value = 20; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(24)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(24)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (24 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"24 bits is 3 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((24 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"24 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(24)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(24)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_24.cs.meta b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_24.cs.meta new file mode 100644 index 00000000000..288ac5a1091 --- /dev/null +++ b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_24.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e11a7f817944ccc4cb4bcf80be0e8bfb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_5.cs b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_5.cs new file mode 100644 index 00000000000..6d9f3a470f6 --- /dev/null +++ b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_5.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY BitCountTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.BitCountAttributeTests.ulong_5 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [BitCount(5)] + [SyncVar] public ulong MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCount(5)] ulong myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCount(5)] + public ulong MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCount(5)] + public ulong MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const ulong value = 20; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(5)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(5)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (5 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"5 bits is 1 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((5 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"5 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(5)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(5)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_5.cs.meta b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_5.cs.meta new file mode 100644 index 00000000000..1a9bbe6a8dd --- /dev/null +++ b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_5.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 46af30f0b26070a40a79fb8bcd03a987 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_64.cs b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_64.cs new file mode 100644 index 00000000000..9c4d3cf6539 --- /dev/null +++ b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_64.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY BitCountTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.BitCountAttributeTests.ulong_64 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [BitCount(64)] + [SyncVar] public ulong MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCount(64)] ulong myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCount(64)] + public ulong MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCount(64)] + public ulong MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const ulong value = 20; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(64)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(64)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (64 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"64 bits is 8 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((64 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"64 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(64)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(64)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_64.cs.meta b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_64.cs.meta new file mode 100644 index 00000000000..1fae091dbaa --- /dev/null +++ b/Assets/Tests/Generated/BitCountTests/BitCountBehaviour_ulong_64.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 20adbacec1c8df644bc7f00d9ca44f2d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/FloatPackTests.meta b/Assets/Tests/Generated/FloatPackTests.meta new file mode 100644 index 00000000000..7894b920595 --- /dev/null +++ b/Assets/Tests/Generated/FloatPackTests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 26357b638cc57df4faada16fce93ea05 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_10.cs b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_10.cs new file mode 100644 index 00000000000..623d65a92a1 --- /dev/null +++ b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_10.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY FloatPackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.FloatPackAttributeTests._100_10 +{ + public class BitPackBehaviour : NetworkBehaviour + { + [FloatPack(100, 0.2f)] + [SyncVar] public float MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([FloatPack(100, 0.2f)] float myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [FloatPack(100, 0.2f)] + public float MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [FloatPack(100, 0.2f)] + public float MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const float value = 5.2f; + private const float within = 0.196f; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(10)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(10)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value).Within(within)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value).Within(within)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (10 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((10 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage.MyValue, Is.EqualTo(inMessage.MyValue).Within(within)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(10)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(10)); + + Assert.That(outStruct.MyValue, Is.EqualTo(inStruct.MyValue).Within(within)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_10.cs.meta b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_10.cs.meta new file mode 100644 index 00000000000..ac255a646f8 --- /dev/null +++ b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_10.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ad89b419431e6004ca48c95535ff2500 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_14.cs b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_14.cs new file mode 100644 index 00000000000..d9b67ba05cb --- /dev/null +++ b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_14.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY FloatPackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.FloatPackAttributeTests._100_14 +{ + public class BitPackBehaviour : NetworkBehaviour + { + [FloatPack(100, 0.02f)] + [SyncVar] public float MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([FloatPack(100, 0.02f)] float myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [FloatPack(100, 0.02f)] + public float MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [FloatPack(100, 0.02f)] + public float MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const float value = 5.2f; + private const float within = 0.0123f; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(14)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(14)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value).Within(within)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value).Within(within)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (14 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"14 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((14 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"14 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage.MyValue, Is.EqualTo(inMessage.MyValue).Within(within)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(14)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(14)); + + Assert.That(outStruct.MyValue, Is.EqualTo(inStruct.MyValue).Within(within)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_14.cs.meta b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_14.cs.meta new file mode 100644 index 00000000000..b7f8135c4a0 --- /dev/null +++ b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_100_14.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 016ea61e12913554fbceaf553a970d39 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_10_10.cs b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_10_10.cs new file mode 100644 index 00000000000..6297ab03137 --- /dev/null +++ b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_10_10.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY FloatPackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.FloatPackAttributeTests._10_10 +{ + public class BitPackBehaviour : NetworkBehaviour + { + [FloatPack(10, 10)] + [SyncVar] public float MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([FloatPack(10, 10)] float myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [FloatPack(10, 10)] + public float MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [FloatPack(10, 10)] + public float MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const float value = 3.3f; + private const float within = 0.0191f; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(10)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(10)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value).Within(within)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value).Within(within)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (10 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((10 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage.MyValue, Is.EqualTo(inMessage.MyValue).Within(within)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(10)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(10)); + + Assert.That(outStruct.MyValue, Is.EqualTo(inStruct.MyValue).Within(within)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_10_10.cs.meta b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_10_10.cs.meta new file mode 100644 index 00000000000..1c0f4e16147 --- /dev/null +++ b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_10_10.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3736f9e65388d0a4dad0c506535044a4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_1_8.cs b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_1_8.cs new file mode 100644 index 00000000000..076e338d43c --- /dev/null +++ b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_1_8.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY FloatPackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.FloatPackAttributeTests._1_8 +{ + public class BitPackBehaviour : NetworkBehaviour + { + [FloatPack(1, 8)] + [SyncVar] public float MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([FloatPack(1, 8)] float myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [FloatPack(1, 8)] + public float MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [FloatPack(1, 8)] + public float MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const float value = 0.2f; + private const float within = 0.00785f; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(8)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(8)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value).Within(within)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value).Within(within)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (8 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"8 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((8 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"8 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage.MyValue, Is.EqualTo(inMessage.MyValue).Within(within)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(8)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(8)); + + Assert.That(outStruct.MyValue, Is.EqualTo(inStruct.MyValue).Within(within)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_1_8.cs.meta b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_1_8.cs.meta new file mode 100644 index 00000000000..0733489db2e --- /dev/null +++ b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_1_8.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 689399c2254fca041840b7084462a871 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_14.cs b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_14.cs new file mode 100644 index 00000000000..e7d20fb3b84 --- /dev/null +++ b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_14.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY FloatPackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.FloatPackAttributeTests._500_14 +{ + public class BitPackBehaviour : NetworkBehaviour + { + [FloatPack(500, 0.1f)] + [SyncVar] public float MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([FloatPack(500, 0.1f)] float myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [FloatPack(500, 0.1f)] + public float MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [FloatPack(500, 0.1f)] + public float MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const float value = 5.2f; + private const float within = 0.0123f; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(14)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(14)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value).Within(within)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value).Within(within)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (14 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"14 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((14 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"14 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage.MyValue, Is.EqualTo(inMessage.MyValue).Within(within)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(14)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(14)); + + Assert.That(outStruct.MyValue, Is.EqualTo(inStruct.MyValue).Within(within)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_14.cs.meta b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_14.cs.meta new file mode 100644 index 00000000000..48629925d93 --- /dev/null +++ b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_14.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d6364115de1c8e541b3e0ff8371fb856 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_17.cs b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_17.cs new file mode 100644 index 00000000000..cbd69fb8a36 --- /dev/null +++ b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_17.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY FloatPackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.FloatPackAttributeTests._500_17 +{ + public class BitPackBehaviour : NetworkBehaviour + { + [FloatPack(500, 0.01f)] + [SyncVar] public float MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([FloatPack(500, 0.01f)] float myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [FloatPack(500, 0.01f)] + public float MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [FloatPack(500, 0.01f)] + public float MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const float value = 5.2f; + private const float within = 0.00763f; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(17)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(17)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value).Within(within)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value).Within(within)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (17 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"17 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((17 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"17 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage.MyValue, Is.EqualTo(inMessage.MyValue).Within(within)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(17)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(17)); + + Assert.That(outStruct.MyValue, Is.EqualTo(inStruct.MyValue).Within(within)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_17.cs.meta b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_17.cs.meta new file mode 100644 index 00000000000..b279434cf5f --- /dev/null +++ b/Assets/Tests/Generated/FloatPackTests/FloatPackBehaviour_500_17.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 38ae51883e12f624d891bd79db46c359 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/QuaternionPackTests.meta b/Assets/Tests/Generated/QuaternionPackTests.meta new file mode 100644 index 00000000000..c6fdc1af174 --- /dev/null +++ b/Assets/Tests/Generated/QuaternionPackTests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d7bca41a46502324099c2f7c0bc703f2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_0.cs b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_0.cs new file mode 100644 index 00000000000..6738f75e853 --- /dev/null +++ b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_0.cs @@ -0,0 +1,178 @@ +// DO NOT EDIT: GENERATED BY QuaternionPackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.QuaternionPackAttributeTests._10_0 +{ + public class BitPackBehaviour : NetworkBehaviour + { + [QuaternionPack(10)] + [SyncVar] public Quaternion MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([QuaternionPack(10)] Quaternion myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [QuaternionPack(10)] + public Quaternion MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [QuaternionPack(10)] + public Quaternion MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Quaternion value = new Quaternion(0f, 0.7071068f, 0f, 0.7071068f); + private const float within = 0.00135f; + + private static void AssertValue(Quaternion actual) + { + Vector3 inVec = value * Vector3.forward; + Vector3 outVec = actual * Vector3.forward; + + // allow for extra within when rotating vector + Assert.AreEqual(inVec.x, outVec.x, within * 2, $"vx off by {Mathf.Abs(inVec.x - outVec.x)}"); + Assert.AreEqual(inVec.y, outVec.y, within * 2, $"vy off by {Mathf.Abs(inVec.y - outVec.y)}"); + Assert.AreEqual(inVec.z, outVec.z, within * 2, $"vz off by {Mathf.Abs(inVec.z - outVec.z)}"); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(32)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(32)); + + AssertValue(clientComponent.MyValue); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (32 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"32 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((32 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"32 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(32)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(32)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_0.cs.meta b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_0.cs.meta new file mode 100644 index 00000000000..c5dc8fb5daf --- /dev/null +++ b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_0.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4ab9fd8937a174e4bb517b25a6feaea1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_45.cs b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_45.cs new file mode 100644 index 00000000000..4f01aab80dc --- /dev/null +++ b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_45.cs @@ -0,0 +1,178 @@ +// DO NOT EDIT: GENERATED BY QuaternionPackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.QuaternionPackAttributeTests._10_45 +{ + public class BitPackBehaviour : NetworkBehaviour + { + [QuaternionPack(10)] + [SyncVar] public Quaternion MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([QuaternionPack(10)] Quaternion myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [QuaternionPack(10)] + public Quaternion MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [QuaternionPack(10)] + public Quaternion MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Quaternion value = new Quaternion(0.2705981f, 0.6532815f, -0.2705981f, 0.6532815f); + private const float within = 0.00135f; + + private static void AssertValue(Quaternion actual) + { + Vector3 inVec = value * Vector3.forward; + Vector3 outVec = actual * Vector3.forward; + + // allow for extra within when rotating vector + Assert.AreEqual(inVec.x, outVec.x, within * 2, $"vx off by {Mathf.Abs(inVec.x - outVec.x)}"); + Assert.AreEqual(inVec.y, outVec.y, within * 2, $"vy off by {Mathf.Abs(inVec.y - outVec.y)}"); + Assert.AreEqual(inVec.z, outVec.z, within * 2, $"vz off by {Mathf.Abs(inVec.z - outVec.z)}"); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(32)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(32)); + + AssertValue(clientComponent.MyValue); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (32 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"32 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((32 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"32 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(32)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(32)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_45.cs.meta b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_45.cs.meta new file mode 100644 index 00000000000..33c2db1a7f6 --- /dev/null +++ b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_10_45.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2aae041c7c6f09e41943dcad2727b69a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_0.cs b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_0.cs new file mode 100644 index 00000000000..c33a44d3ed5 --- /dev/null +++ b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_0.cs @@ -0,0 +1,178 @@ +// DO NOT EDIT: GENERATED BY QuaternionPackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.QuaternionPackAttributeTests._8_0 +{ + public class BitPackBehaviour : NetworkBehaviour + { + [QuaternionPack(8)] + [SyncVar] public Quaternion MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([QuaternionPack(8)] Quaternion myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [QuaternionPack(8)] + public Quaternion MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [QuaternionPack(8)] + public Quaternion MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Quaternion value = new Quaternion(0f, 0.7071068f, 0f, 0.7071068f); + private const float within = 0.0054f; + + private static void AssertValue(Quaternion actual) + { + Vector3 inVec = value * Vector3.forward; + Vector3 outVec = actual * Vector3.forward; + + // allow for extra within when rotating vector + Assert.AreEqual(inVec.x, outVec.x, within * 2, $"vx off by {Mathf.Abs(inVec.x - outVec.x)}"); + Assert.AreEqual(inVec.y, outVec.y, within * 2, $"vy off by {Mathf.Abs(inVec.y - outVec.y)}"); + Assert.AreEqual(inVec.z, outVec.z, within * 2, $"vz off by {Mathf.Abs(inVec.z - outVec.z)}"); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(26)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(26)); + + AssertValue(clientComponent.MyValue); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (26 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"26 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((26 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"26 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(26)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(26)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_0.cs.meta b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_0.cs.meta new file mode 100644 index 00000000000..dea1117b642 --- /dev/null +++ b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_0.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9ee00da2473d29a4699af571979e004a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_45.cs b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_45.cs new file mode 100644 index 00000000000..20c633d882b --- /dev/null +++ b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_45.cs @@ -0,0 +1,178 @@ +// DO NOT EDIT: GENERATED BY QuaternionPackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.QuaternionPackAttributeTests._8_45 +{ + public class BitPackBehaviour : NetworkBehaviour + { + [QuaternionPack(8)] + [SyncVar] public Quaternion MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([QuaternionPack(8)] Quaternion myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [QuaternionPack(8)] + public Quaternion MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [QuaternionPack(8)] + public Quaternion MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Quaternion value = new Quaternion(0.2705981f, 0.6532815f, -0.2705981f, 0.6532815f); + private const float within = 0.0054f; + + private static void AssertValue(Quaternion actual) + { + Vector3 inVec = value * Vector3.forward; + Vector3 outVec = actual * Vector3.forward; + + // allow for extra within when rotating vector + Assert.AreEqual(inVec.x, outVec.x, within * 2, $"vx off by {Mathf.Abs(inVec.x - outVec.x)}"); + Assert.AreEqual(inVec.y, outVec.y, within * 2, $"vy off by {Mathf.Abs(inVec.y - outVec.y)}"); + Assert.AreEqual(inVec.z, outVec.z, within * 2, $"vz off by {Mathf.Abs(inVec.z - outVec.z)}"); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(26)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(26)); + + AssertValue(clientComponent.MyValue); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (26 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"26 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((26 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"26 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(26)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(26)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_45.cs.meta b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_45.cs.meta new file mode 100644 index 00000000000..9e86c248805 --- /dev/null +++ b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_8_45.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ff61adfaa015039468c52bd397fbb3b6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_0.cs b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_0.cs new file mode 100644 index 00000000000..9d86cd89686 --- /dev/null +++ b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_0.cs @@ -0,0 +1,178 @@ +// DO NOT EDIT: GENERATED BY QuaternionPackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.QuaternionPackAttributeTests._9_0 +{ + public class BitPackBehaviour : NetworkBehaviour + { + [QuaternionPack(9)] + [SyncVar] public Quaternion MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([QuaternionPack(9)] Quaternion myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [QuaternionPack(9)] + public Quaternion MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [QuaternionPack(9)] + public Quaternion MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Quaternion value = new Quaternion(0f, 0.7071068f, 0f, 0.7071068f); + private const float within = 0.0027f; + + private static void AssertValue(Quaternion actual) + { + Vector3 inVec = value * Vector3.forward; + Vector3 outVec = actual * Vector3.forward; + + // allow for extra within when rotating vector + Assert.AreEqual(inVec.x, outVec.x, within * 2, $"vx off by {Mathf.Abs(inVec.x - outVec.x)}"); + Assert.AreEqual(inVec.y, outVec.y, within * 2, $"vy off by {Mathf.Abs(inVec.y - outVec.y)}"); + Assert.AreEqual(inVec.z, outVec.z, within * 2, $"vz off by {Mathf.Abs(inVec.z - outVec.z)}"); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(29)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(29)); + + AssertValue(clientComponent.MyValue); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (29 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"29 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((29 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"29 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(29)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(29)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_0.cs.meta b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_0.cs.meta new file mode 100644 index 00000000000..796d984c61f --- /dev/null +++ b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_0.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e96f6231777d2634783d2c5b3f01d2ae +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_45.cs b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_45.cs new file mode 100644 index 00000000000..8701284d1c1 --- /dev/null +++ b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_45.cs @@ -0,0 +1,178 @@ +// DO NOT EDIT: GENERATED BY QuaternionPackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.QuaternionPackAttributeTests._9_45 +{ + public class BitPackBehaviour : NetworkBehaviour + { + [QuaternionPack(9)] + [SyncVar] public Quaternion MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([QuaternionPack(9)] Quaternion myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [QuaternionPack(9)] + public Quaternion MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [QuaternionPack(9)] + public Quaternion MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Quaternion value = new Quaternion(0.2705981f, 0.6532815f, -0.2705981f, 0.6532815f); + private const float within = 0.0027f; + + private static void AssertValue(Quaternion actual) + { + Vector3 inVec = value * Vector3.forward; + Vector3 outVec = actual * Vector3.forward; + + // allow for extra within when rotating vector + Assert.AreEqual(inVec.x, outVec.x, within * 2, $"vx off by {Mathf.Abs(inVec.x - outVec.x)}"); + Assert.AreEqual(inVec.y, outVec.y, within * 2, $"vy off by {Mathf.Abs(inVec.y - outVec.y)}"); + Assert.AreEqual(inVec.z, outVec.z, within * 2, $"vz off by {Mathf.Abs(inVec.z - outVec.z)}"); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(29)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(29)); + + AssertValue(clientComponent.MyValue); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (29 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"29 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((29 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"29 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(29)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(29)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_45.cs.meta b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_45.cs.meta new file mode 100644 index 00000000000..be847736caa --- /dev/null +++ b/Assets/Tests/Generated/QuaternionPackTests/QuaternionPackBehaviour_9_45.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eb3e6e061e904fc45abc3bfc793042db +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntBlocksTests.meta b/Assets/Tests/Generated/VarIntBlocksTests.meta new file mode 100644 index 00000000000..3cc414c7628 --- /dev/null +++ b/Assets/Tests/Generated/VarIntBlocksTests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 54cdd4dda64543b41b134919dfe5f4ca +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnumByte_4.cs b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnumByte_4.cs new file mode 100644 index 00000000000..473f6fe1cc7 --- /dev/null +++ b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnumByte_4.cs @@ -0,0 +1,203 @@ +// DO NOT EDIT: GENERATED BY VarIntBlocksTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.VarIntBlocksTests.MyEnumByte_4 +{ + [System.Flags, System.Serializable] + public enum MyEnumByte : byte + { + None = 0, + HasHealth = 1, + HasArmor = 2, + HasGun = 4, + HasAmmo = 8, + HasLeftHand = 16, + HasRightHand = 32, + HasHead = 64, + } + public class BitPackBehaviour : NetworkBehaviour + { + [VarIntBlocks(4)] + [SyncVar] public MyEnumByte MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([VarIntBlocks(4)] MyEnumByte myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [VarIntBlocks(4)] + public MyEnumByte MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [VarIntBlocks(4)] + public MyEnumByte MyValue; + } + + public class BitPackTest : ClientServerSetup + { + public struct TestCase + { + public MyEnumByte value; + public int expectedBits; + public override string ToString() => value.ToString(); + } + + private static TestCase[] cases = new TestCase[] + { + new TestCase { value = (MyEnumByte)0, expectedBits = 5 }, + new TestCase { value = (MyEnumByte)4, expectedBits = 5 }, + new TestCase { value = (MyEnumByte)16, expectedBits = 10 }, + new TestCase { value = (MyEnumByte)64, expectedBits = 10 } + }; + + [Test] + public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + MyEnumByte value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + MyEnumByte value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (expectedBitCount + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + MyEnumByte value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + MyEnumByte value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnumByte_4.cs.meta b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnumByte_4.cs.meta new file mode 100644 index 00000000000..7370d76690e --- /dev/null +++ b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnumByte_4.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dd58ec5b813985049bc40fec3e8634ba +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnum_4.cs b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnum_4.cs new file mode 100644 index 00000000000..5d10b8a2965 --- /dev/null +++ b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnum_4.cs @@ -0,0 +1,203 @@ +// DO NOT EDIT: GENERATED BY VarIntBlocksTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.VarIntBlocksTests.MyEnum_4 +{ + [System.Flags, System.Serializable] + public enum MyEnum + { + None = 0, + HasHealth = 1, + HasArmor = 2, + HasGun = 4, + HasAmmo = 8, + HasLeftHand = 16, + HasRightHand = 32, + HasHead = 64, + } + public class BitPackBehaviour : NetworkBehaviour + { + [VarIntBlocks(4)] + [SyncVar] public MyEnum MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([VarIntBlocks(4)] MyEnum myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [VarIntBlocks(4)] + public MyEnum MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [VarIntBlocks(4)] + public MyEnum MyValue; + } + + public class BitPackTest : ClientServerSetup + { + public struct TestCase + { + public MyEnum value; + public int expectedBits; + public override string ToString() => value.ToString(); + } + + private static TestCase[] cases = new TestCase[] + { + new TestCase { value = (MyEnum)0, expectedBits = 5 }, + new TestCase { value = (MyEnum)4, expectedBits = 5 }, + new TestCase { value = (MyEnum)16, expectedBits = 10 }, + new TestCase { value = (MyEnum)64, expectedBits = 10 } + }; + + [Test] + public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + MyEnum value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + MyEnum value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (expectedBitCount + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + MyEnum value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + MyEnum value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnum_4.cs.meta b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnum_4.cs.meta new file mode 100644 index 00000000000..0a48a27de04 --- /dev/null +++ b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_MyEnum_4.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8564970176bb5a343aa8762c1b5daa19 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_6.cs b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_6.cs new file mode 100644 index 00000000000..a0bbd738c0d --- /dev/null +++ b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_6.cs @@ -0,0 +1,192 @@ +// DO NOT EDIT: GENERATED BY VarIntBlocksTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.VarIntBlocksTests.int_6 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [VarIntBlocks(6)] + [SyncVar] public int MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([VarIntBlocks(6)] int myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [VarIntBlocks(6)] + public int MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [VarIntBlocks(6)] + public int MyValue; + } + + public class BitPackTest : ClientServerSetup + { + public struct TestCase + { + public int value; + public int expectedBits; + public override string ToString() => value.ToString(); + } + + private static TestCase[] cases = new TestCase[] + { + new TestCase { value = 10, expectedBits = 7 }, + new TestCase { value = 100, expectedBits = 14 }, + new TestCase { value = 1000, expectedBits = 14 }, + new TestCase { value = 10000, expectedBits = 21 } + }; + + [Test] + public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + int value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + int value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (expectedBitCount + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + int value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + int value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_6.cs.meta b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_6.cs.meta new file mode 100644 index 00000000000..ef4fb1dd5dc --- /dev/null +++ b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_6.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c633b38fb69d241479d23a9938cbde2a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_7.cs b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_7.cs new file mode 100644 index 00000000000..8a92fcaf14f --- /dev/null +++ b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_7.cs @@ -0,0 +1,191 @@ +// DO NOT EDIT: GENERATED BY VarIntBlocksTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.VarIntBlocksTests.int_7 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [VarIntBlocks(7)] + [SyncVar] public int MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([VarIntBlocks(7)] int myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [VarIntBlocks(7)] + public int MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [VarIntBlocks(7)] + public int MyValue; + } + + public class BitPackTest : ClientServerSetup + { + public struct TestCase + { + public int value; + public int expectedBits; + public override string ToString() => value.ToString(); + } + + private static TestCase[] cases = new TestCase[] + { + new TestCase { value = 10, expectedBits = 8 }, + new TestCase { value = 100, expectedBits = 8 }, + new TestCase { value = 1000, expectedBits = 16 } + }; + + [Test] + public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + int value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + int value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (expectedBitCount + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + int value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + int value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_7.cs.meta b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_7.cs.meta new file mode 100644 index 00000000000..3e1ae7224f3 --- /dev/null +++ b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_int_7.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0ed4343ce13b5974fb968b088630ef72 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_long_8.cs b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_long_8.cs new file mode 100644 index 00000000000..84771f20fb6 --- /dev/null +++ b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_long_8.cs @@ -0,0 +1,192 @@ +// DO NOT EDIT: GENERATED BY VarIntBlocksTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.VarIntBlocksTests.long_8 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [VarIntBlocks(8)] + [SyncVar] public long MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([VarIntBlocks(8)] long myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [VarIntBlocks(8)] + public long MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [VarIntBlocks(8)] + public long MyValue; + } + + public class BitPackTest : ClientServerSetup + { + public struct TestCase + { + public long value; + public int expectedBits; + public override string ToString() => value.ToString(); + } + + private static TestCase[] cases = new TestCase[] + { + new TestCase { value = 10L, expectedBits = 9 }, + new TestCase { value = 100L, expectedBits = 9 }, + new TestCase { value = 1000L, expectedBits = 18 }, + new TestCase { value = 10000L, expectedBits = 18 } + }; + + [Test] + public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + long value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + long value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (expectedBitCount + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + long value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + long value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_long_8.cs.meta b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_long_8.cs.meta new file mode 100644 index 00000000000..95f0c3b0423 --- /dev/null +++ b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_long_8.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 512c8abf0775f604d96957c9a852b386 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_short_6.cs b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_short_6.cs new file mode 100644 index 00000000000..acd4ee552a9 --- /dev/null +++ b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_short_6.cs @@ -0,0 +1,192 @@ +// DO NOT EDIT: GENERATED BY VarIntBlocksTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.VarIntBlocksTests.short_6 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [VarIntBlocks(6)] + [SyncVar] public short MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([VarIntBlocks(6)] short myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [VarIntBlocks(6)] + public short MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [VarIntBlocks(6)] + public short MyValue; + } + + public class BitPackTest : ClientServerSetup + { + public struct TestCase + { + public short value; + public int expectedBits; + public override string ToString() => value.ToString(); + } + + private static TestCase[] cases = new TestCase[] + { + new TestCase { value = (short)10, expectedBits = 7 }, + new TestCase { value = (short)100, expectedBits = 14 }, + new TestCase { value = (short)1000, expectedBits = 14 }, + new TestCase { value = (short)10000, expectedBits = 21 } + }; + + [Test] + public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + short value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + short value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (expectedBitCount + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + short value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + short value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_short_6.cs.meta b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_short_6.cs.meta new file mode 100644 index 00000000000..8abbae4ab18 --- /dev/null +++ b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_short_6.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6de4b164f86fb5b4f80251d7e2ba68f2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_6.cs b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_6.cs new file mode 100644 index 00000000000..2b9ba9be853 --- /dev/null +++ b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_6.cs @@ -0,0 +1,192 @@ +// DO NOT EDIT: GENERATED BY VarIntBlocksTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.VarIntBlocksTests.uint_6 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [VarIntBlocks(6)] + [SyncVar] public uint MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([VarIntBlocks(6)] uint myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [VarIntBlocks(6)] + public uint MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [VarIntBlocks(6)] + public uint MyValue; + } + + public class BitPackTest : ClientServerSetup + { + public struct TestCase + { + public uint value; + public int expectedBits; + public override string ToString() => value.ToString(); + } + + private static TestCase[] cases = new TestCase[] + { + new TestCase { value = 170U, expectedBits = 14 }, + new TestCase { value = 500U, expectedBits = 14 }, + new TestCase { value = 15000U, expectedBits = 21 }, + new TestCase { value = 50000U, expectedBits = 21 } + }; + + [Test] + public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + uint value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + uint value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (expectedBitCount + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + uint value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + uint value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_6.cs.meta b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_6.cs.meta new file mode 100644 index 00000000000..6ce35e9fdd7 --- /dev/null +++ b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_6.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: faffd93deb00a404ab43df3a310408af +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_7.cs b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_7.cs new file mode 100644 index 00000000000..c0ca24e1154 --- /dev/null +++ b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_7.cs @@ -0,0 +1,192 @@ +// DO NOT EDIT: GENERATED BY VarIntBlocksTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.VarIntBlocksTests.uint_7 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [VarIntBlocks(7)] + [SyncVar] public uint MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([VarIntBlocks(7)] uint myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [VarIntBlocks(7)] + public uint MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [VarIntBlocks(7)] + public uint MyValue; + } + + public class BitPackTest : ClientServerSetup + { + public struct TestCase + { + public uint value; + public int expectedBits; + public override string ToString() => value.ToString(); + } + + private static TestCase[] cases = new TestCase[] + { + new TestCase { value = 10U, expectedBits = 8 }, + new TestCase { value = 100U, expectedBits = 8 }, + new TestCase { value = 1000U, expectedBits = 16 }, + new TestCase { value = 10000U, expectedBits = 16 } + }; + + [Test] + public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + uint value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + uint value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (expectedBitCount + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + uint value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + uint value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_7.cs.meta b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_7.cs.meta new file mode 100644 index 00000000000..2a12a3f452e --- /dev/null +++ b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_7.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0bd2a4ccf2c2b304a9e8ff665cd01f0f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_8.cs b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_8.cs new file mode 100644 index 00000000000..124ea14a670 --- /dev/null +++ b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_8.cs @@ -0,0 +1,193 @@ +// DO NOT EDIT: GENERATED BY VarIntBlocksTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.VarIntBlocksTests.uint_8 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [VarIntBlocks(8)] + [SyncVar] public uint MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([VarIntBlocks(8)] uint myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [VarIntBlocks(8)] + public uint MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [VarIntBlocks(8)] + public uint MyValue; + } + + public class BitPackTest : ClientServerSetup + { + public struct TestCase + { + public uint value; + public int expectedBits; + public override string ToString() => value.ToString(); + } + + private static TestCase[] cases = new TestCase[] + { + new TestCase { value = 170U, expectedBits = 9 }, + new TestCase { value = 500U, expectedBits = 18 }, + new TestCase { value = 15000U, expectedBits = 18 }, + new TestCase { value = 50000U, expectedBits = 18 }, + new TestCase { value = 400000U, expectedBits = 27 } + }; + + [Test] + public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + uint value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + uint value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (expectedBitCount + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + uint value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + uint value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_8.cs.meta b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_8.cs.meta new file mode 100644 index 00000000000..3e75596f1c2 --- /dev/null +++ b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_uint_8.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7f9207744d86d874a9eb7f04770fe84b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ulong_9.cs b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ulong_9.cs new file mode 100644 index 00000000000..7ef1f03f6f5 --- /dev/null +++ b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ulong_9.cs @@ -0,0 +1,192 @@ +// DO NOT EDIT: GENERATED BY VarIntBlocksTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.VarIntBlocksTests.ulong_9 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [VarIntBlocks(9)] + [SyncVar] public ulong MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([VarIntBlocks(9)] ulong myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [VarIntBlocks(9)] + public ulong MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [VarIntBlocks(9)] + public ulong MyValue; + } + + public class BitPackTest : ClientServerSetup + { + public struct TestCase + { + public ulong value; + public int expectedBits; + public override string ToString() => value.ToString(); + } + + private static TestCase[] cases = new TestCase[] + { + new TestCase { value = 10UL, expectedBits = 10 }, + new TestCase { value = 100UL, expectedBits = 10 }, + new TestCase { value = 1000UL, expectedBits = 20 }, + new TestCase { value = 10000UL, expectedBits = 20 } + }; + + [Test] + public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + ulong value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + ulong value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (expectedBitCount + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + ulong value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + ulong value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ulong_9.cs.meta b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ulong_9.cs.meta new file mode 100644 index 00000000000..69489e41e43 --- /dev/null +++ b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ulong_9.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f530467d1062c8e4e979086df243dab5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ushort_7.cs b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ushort_7.cs new file mode 100644 index 00000000000..54e4e664cec --- /dev/null +++ b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ushort_7.cs @@ -0,0 +1,192 @@ +// DO NOT EDIT: GENERATED BY VarIntBlocksTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.VarIntBlocksTests.ushort_7 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [VarIntBlocks(7)] + [SyncVar] public ushort MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([VarIntBlocks(7)] ushort myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [VarIntBlocks(7)] + public ushort MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [VarIntBlocks(7)] + public ushort MyValue; + } + + public class BitPackTest : ClientServerSetup + { + public struct TestCase + { + public ushort value; + public int expectedBits; + public override string ToString() => value.ToString(); + } + + private static TestCase[] cases = new TestCase[] + { + new TestCase { value = (ushort)10, expectedBits = 8 }, + new TestCase { value = (ushort)100, expectedBits = 8 }, + new TestCase { value = (ushort)1000, expectedBits = 16 }, + new TestCase { value = (ushort)10000, expectedBits = 16 } + }; + + [Test] + public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + ushort value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + ushort value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (expectedBitCount + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + ushort value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + ushort value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ushort_7.cs.meta b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ushort_7.cs.meta new file mode 100644 index 00000000000..2f7e7ea744d --- /dev/null +++ b/Assets/Tests/Generated/VarIntBlocksTests/VarIntBlocksBehaviour_ushort_7.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b533f40ccf5335246ba9f2ea00e9a25b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntTests.meta b/Assets/Tests/Generated/VarIntTests.meta new file mode 100644 index 00000000000..01a378d165f --- /dev/null +++ b/Assets/Tests/Generated/VarIntTests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b563527bc175b924d9137fbc7b401a4a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnumByte_4_64.cs b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnumByte_4_64.cs new file mode 100644 index 00000000000..2e754612c58 --- /dev/null +++ b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnumByte_4_64.cs @@ -0,0 +1,203 @@ +// DO NOT EDIT: GENERATED BY VarIntTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.VarIntTests.MyEnumByte_4_64 +{ + [System.Flags, System.Serializable] + public enum MyEnumByte : byte + { + None = 0, + HasHealth = 1, + HasArmor = 2, + HasGun = 4, + HasAmmo = 8, + HasLeftHand = 16, + HasRightHand = 32, + HasHead = 64, + } + public class BitPackBehaviour : NetworkBehaviour + { + [VarInt(4, 64)] + [SyncVar] public MyEnumByte MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([VarInt(4, 64)] MyEnumByte myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [VarInt(4, 64)] + public MyEnumByte MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [VarInt(4, 64)] + public MyEnumByte MyValue; + } + + public class BitPackTest : ClientServerSetup + { + public struct TestCase + { + public MyEnumByte value; + public int expectedBits; + public override string ToString() => value.ToString(); + } + + private static TestCase[] cases = new TestCase[] + { + new TestCase { value = (MyEnumByte)0, expectedBits = 4 }, + new TestCase { value = (MyEnumByte)4, expectedBits = 4 }, + new TestCase { value = (MyEnumByte)16, expectedBits = 9 }, + new TestCase { value = (MyEnumByte)64, expectedBits = 9 } + }; + + [Test] + public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + MyEnumByte value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + MyEnumByte value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (expectedBitCount + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + MyEnumByte value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + MyEnumByte value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnumByte_4_64.cs.meta b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnumByte_4_64.cs.meta new file mode 100644 index 00000000000..9d7e52352e4 --- /dev/null +++ b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnumByte_4_64.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 058da6460bf60964e80bdfaa3e0483e3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnum_4_64.cs b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnum_4_64.cs new file mode 100644 index 00000000000..f0634e5f446 --- /dev/null +++ b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnum_4_64.cs @@ -0,0 +1,203 @@ +// DO NOT EDIT: GENERATED BY VarIntTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.VarIntTests.MyEnum_4_64 +{ + [System.Flags, System.Serializable] + public enum MyEnum + { + None = 0, + HasHealth = 1, + HasArmor = 2, + HasGun = 4, + HasAmmo = 8, + HasLeftHand = 16, + HasRightHand = 32, + HasHead = 64, + } + public class BitPackBehaviour : NetworkBehaviour + { + [VarInt(4, 64)] + [SyncVar] public MyEnum MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([VarInt(4, 64)] MyEnum myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [VarInt(4, 64)] + public MyEnum MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [VarInt(4, 64)] + public MyEnum MyValue; + } + + public class BitPackTest : ClientServerSetup + { + public struct TestCase + { + public MyEnum value; + public int expectedBits; + public override string ToString() => value.ToString(); + } + + private static TestCase[] cases = new TestCase[] + { + new TestCase { value = (MyEnum)0, expectedBits = 4 }, + new TestCase { value = (MyEnum)4, expectedBits = 4 }, + new TestCase { value = (MyEnum)16, expectedBits = 9 }, + new TestCase { value = (MyEnum)64, expectedBits = 9 } + }; + + [Test] + public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + MyEnum value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + MyEnum value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (expectedBitCount + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + MyEnum value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + MyEnum value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnum_4_64.cs.meta b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnum_4_64.cs.meta new file mode 100644 index 00000000000..631a6f0275a --- /dev/null +++ b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_MyEnum_4_64.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ae539f5208a0fdb419186c45080bf2a9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_1000.cs b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_1000.cs new file mode 100644 index 00000000000..30759b27ade --- /dev/null +++ b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_1000.cs @@ -0,0 +1,192 @@ +// DO NOT EDIT: GENERATED BY VarIntTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.VarIntTests.int_100_1000 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [VarInt(100, 1000, 10000)] + [SyncVar] public int MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([VarInt(100, 1000, 10000)] int myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [VarInt(100, 1000, 10000)] + public int MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [VarInt(100, 1000, 10000)] + public int MyValue; + } + + public class BitPackTest : ClientServerSetup + { + public struct TestCase + { + public int value; + public int expectedBits; + public override string ToString() => value.ToString(); + } + + private static TestCase[] cases = new TestCase[] + { + new TestCase { value = 10, expectedBits = 8 }, + new TestCase { value = 100, expectedBits = 8 }, + new TestCase { value = 1000, expectedBits = 12 }, + new TestCase { value = 10000, expectedBits = 16 } + }; + + [Test] + public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + int value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + int value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (expectedBitCount + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + int value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + int value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_1000.cs.meta b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_1000.cs.meta new file mode 100644 index 00000000000..09f3e4de21b --- /dev/null +++ b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_1000.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f3ea125124510384f8cec89825e54ebc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_10000.cs b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_10000.cs new file mode 100644 index 00000000000..659913559e4 --- /dev/null +++ b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_10000.cs @@ -0,0 +1,191 @@ +// DO NOT EDIT: GENERATED BY VarIntTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.VarIntTests.int_100_10000 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [VarInt(100, 10000)] + [SyncVar] public int MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([VarInt(100, 10000)] int myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [VarInt(100, 10000)] + public int MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [VarInt(100, 10000)] + public int MyValue; + } + + public class BitPackTest : ClientServerSetup + { + public struct TestCase + { + public int value; + public int expectedBits; + public override string ToString() => value.ToString(); + } + + private static TestCase[] cases = new TestCase[] + { + new TestCase { value = 10, expectedBits = 8 }, + new TestCase { value = 100, expectedBits = 8 }, + new TestCase { value = 1000, expectedBits = 16 } + }; + + [Test] + public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + int value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + int value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (expectedBitCount + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + int value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + int value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_10000.cs.meta b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_10000.cs.meta new file mode 100644 index 00000000000..de2e4028be1 --- /dev/null +++ b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_int_100_10000.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bcf75fcd76c9e4046bfe4e219d3db071 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_long_100_1000.cs b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_long_100_1000.cs new file mode 100644 index 00000000000..c19d0b63322 --- /dev/null +++ b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_long_100_1000.cs @@ -0,0 +1,192 @@ +// DO NOT EDIT: GENERATED BY VarIntTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.VarIntTests.long_100_1000 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [VarInt(100, 1000, 10000)] + [SyncVar] public long MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([VarInt(100, 1000, 10000)] long myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [VarInt(100, 1000, 10000)] + public long MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [VarInt(100, 1000, 10000)] + public long MyValue; + } + + public class BitPackTest : ClientServerSetup + { + public struct TestCase + { + public long value; + public int expectedBits; + public override string ToString() => value.ToString(); + } + + private static TestCase[] cases = new TestCase[] + { + new TestCase { value = 10L, expectedBits = 8 }, + new TestCase { value = 100L, expectedBits = 8 }, + new TestCase { value = 1000L, expectedBits = 12 }, + new TestCase { value = 10000L, expectedBits = 16 } + }; + + [Test] + public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + long value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + long value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (expectedBitCount + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + long value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + long value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_long_100_1000.cs.meta b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_long_100_1000.cs.meta new file mode 100644 index 00000000000..61e222c1673 --- /dev/null +++ b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_long_100_1000.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 05bd3b7b99eeb7b41a045b4585b0c494 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_short_100_1000.cs b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_short_100_1000.cs new file mode 100644 index 00000000000..6c068882534 --- /dev/null +++ b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_short_100_1000.cs @@ -0,0 +1,192 @@ +// DO NOT EDIT: GENERATED BY VarIntTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.VarIntTests.short_100_1000 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [VarInt(100, 1000, 10000)] + [SyncVar] public short MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([VarInt(100, 1000, 10000)] short myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [VarInt(100, 1000, 10000)] + public short MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [VarInt(100, 1000, 10000)] + public short MyValue; + } + + public class BitPackTest : ClientServerSetup + { + public struct TestCase + { + public short value; + public int expectedBits; + public override string ToString() => value.ToString(); + } + + private static TestCase[] cases = new TestCase[] + { + new TestCase { value = (short)10, expectedBits = 8 }, + new TestCase { value = (short)100, expectedBits = 8 }, + new TestCase { value = (short)1000, expectedBits = 12 }, + new TestCase { value = (short)10000, expectedBits = 16 } + }; + + [Test] + public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + short value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + short value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (expectedBitCount + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + short value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + short value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_short_100_1000.cs.meta b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_short_100_1000.cs.meta new file mode 100644 index 00000000000..1b94954749b --- /dev/null +++ b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_short_100_1000.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5b8e7ddcab5766b42837e2e90993cc86 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_100_1000.cs b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_100_1000.cs new file mode 100644 index 00000000000..daa5017cf1b --- /dev/null +++ b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_100_1000.cs @@ -0,0 +1,192 @@ +// DO NOT EDIT: GENERATED BY VarIntTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.VarIntTests.uint_100_1000 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [VarInt(100, 1000, 10000)] + [SyncVar] public uint MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([VarInt(100, 1000, 10000)] uint myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [VarInt(100, 1000, 10000)] + public uint MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [VarInt(100, 1000, 10000)] + public uint MyValue; + } + + public class BitPackTest : ClientServerSetup + { + public struct TestCase + { + public uint value; + public int expectedBits; + public override string ToString() => value.ToString(); + } + + private static TestCase[] cases = new TestCase[] + { + new TestCase { value = 10U, expectedBits = 8 }, + new TestCase { value = 100U, expectedBits = 8 }, + new TestCase { value = 1000U, expectedBits = 12 }, + new TestCase { value = 10000U, expectedBits = 16 } + }; + + [Test] + public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + uint value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + uint value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (expectedBitCount + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + uint value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + uint value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_100_1000.cs.meta b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_100_1000.cs.meta new file mode 100644 index 00000000000..dc77a568c58 --- /dev/null +++ b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_100_1000.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2e31b41d99f9064419329854894d1f3f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_255_64000.cs b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_255_64000.cs new file mode 100644 index 00000000000..abac9ac460b --- /dev/null +++ b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_255_64000.cs @@ -0,0 +1,192 @@ +// DO NOT EDIT: GENERATED BY VarIntTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.VarIntTests.uint_255_64000 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [VarInt(255, 64000)] + [SyncVar] public uint MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([VarInt(255, 64000)] uint myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [VarInt(255, 64000)] + public uint MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [VarInt(255, 64000)] + public uint MyValue; + } + + public class BitPackTest : ClientServerSetup + { + public struct TestCase + { + public uint value; + public int expectedBits; + public override string ToString() => value.ToString(); + } + + private static TestCase[] cases = new TestCase[] + { + new TestCase { value = 170U, expectedBits = 9 }, + new TestCase { value = 500U, expectedBits = 18 }, + new TestCase { value = 15000U, expectedBits = 18 }, + new TestCase { value = 50000U, expectedBits = 18 } + }; + + [Test] + public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + uint value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + uint value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (expectedBitCount + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + uint value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + uint value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_255_64000.cs.meta b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_255_64000.cs.meta new file mode 100644 index 00000000000..816caf767be --- /dev/null +++ b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_255_64000.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5c62e5784039a1d4283f53d29814edac +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_500_32000.cs b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_500_32000.cs new file mode 100644 index 00000000000..f7614474ab1 --- /dev/null +++ b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_500_32000.cs @@ -0,0 +1,193 @@ +// DO NOT EDIT: GENERATED BY VarIntTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.VarIntTests.uint_500_32000 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [VarInt(500, 32000, 2000000)] + [SyncVar] public uint MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([VarInt(500, 32000, 2000000)] uint myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [VarInt(500, 32000, 2000000)] + public uint MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [VarInt(500, 32000, 2000000)] + public uint MyValue; + } + + public class BitPackTest : ClientServerSetup + { + public struct TestCase + { + public uint value; + public int expectedBits; + public override string ToString() => value.ToString(); + } + + private static TestCase[] cases = new TestCase[] + { + new TestCase { value = 170U, expectedBits = 10 }, + new TestCase { value = 500U, expectedBits = 10 }, + new TestCase { value = 15000U, expectedBits = 17 }, + new TestCase { value = 50000U, expectedBits = 23 }, + new TestCase { value = 400000U, expectedBits = 23 } + }; + + [Test] + public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + uint value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + uint value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (expectedBitCount + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + uint value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + uint value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_500_32000.cs.meta b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_500_32000.cs.meta new file mode 100644 index 00000000000..1bc64c1763f --- /dev/null +++ b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_uint_500_32000.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9afb405e75120264aad8abce8467336c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ulong_100_1000.cs b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ulong_100_1000.cs new file mode 100644 index 00000000000..5333b9d2295 --- /dev/null +++ b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ulong_100_1000.cs @@ -0,0 +1,192 @@ +// DO NOT EDIT: GENERATED BY VarIntTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.VarIntTests.ulong_100_1000 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [VarInt(100, 1000, 10000)] + [SyncVar] public ulong MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([VarInt(100, 1000, 10000)] ulong myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [VarInt(100, 1000, 10000)] + public ulong MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [VarInt(100, 1000, 10000)] + public ulong MyValue; + } + + public class BitPackTest : ClientServerSetup + { + public struct TestCase + { + public ulong value; + public int expectedBits; + public override string ToString() => value.ToString(); + } + + private static TestCase[] cases = new TestCase[] + { + new TestCase { value = 10UL, expectedBits = 8 }, + new TestCase { value = 100UL, expectedBits = 8 }, + new TestCase { value = 1000UL, expectedBits = 12 }, + new TestCase { value = 10000UL, expectedBits = 16 } + }; + + [Test] + public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + ulong value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + ulong value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (expectedBitCount + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + ulong value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + ulong value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ulong_100_1000.cs.meta b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ulong_100_1000.cs.meta new file mode 100644 index 00000000000..9e693f73501 --- /dev/null +++ b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ulong_100_1000.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d86a0360c302b1743b3d81fd839cee80 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ushort_100_1000.cs b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ushort_100_1000.cs new file mode 100644 index 00000000000..94bfa391e49 --- /dev/null +++ b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ushort_100_1000.cs @@ -0,0 +1,192 @@ +// DO NOT EDIT: GENERATED BY VarIntTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.VarIntTests.ushort_100_1000 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [VarInt(100, 1000, 10000)] + [SyncVar] public ushort MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([VarInt(100, 1000, 10000)] ushort myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [VarInt(100, 1000, 10000)] + public ushort MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [VarInt(100, 1000, 10000)] + public ushort MyValue; + } + + public class BitPackTest : ClientServerSetup + { + public struct TestCase + { + public ushort value; + public int expectedBits; + public override string ToString() => value.ToString(); + } + + private static TestCase[] cases = new TestCase[] + { + new TestCase { value = (ushort)10, expectedBits = 8 }, + new TestCase { value = (ushort)100, expectedBits = 8 }, + new TestCase { value = (ushort)1000, expectedBits = 12 }, + new TestCase { value = (ushort)10000, expectedBits = 16 } + }; + + [Test] + public void SyncVarIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + ushort value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + ushort value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (expectedBitCount + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"expectedBitCount bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + ushort value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((expectedBitCount + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"{expectedBitCount} bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked([ValueSource(nameof(cases))] TestCase TestCase) + { + ushort value = TestCase.value; + int expectedBitCount = TestCase.expectedBits; + + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(expectedBitCount)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(expectedBitCount)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ushort_100_1000.cs.meta b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ushort_100_1000.cs.meta new file mode 100644 index 00000000000..e33e9c038e2 --- /dev/null +++ b/Assets/Tests/Generated/VarIntTests/VarIntBehaviour_ushort_100_1000.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 80ae2ac144aa18444a2b09c45395c714 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector2PackTests.meta b/Assets/Tests/Generated/Vector2PackTests.meta new file mode 100644 index 00000000000..d0b299f5bdf --- /dev/null +++ b/Assets/Tests/Generated/Vector2PackTests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 799eefc3ace83a8419c274dc0e7e875e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27b2.cs b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27b2.cs new file mode 100644 index 00000000000..81ce7ce5009 --- /dev/null +++ b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27b2.cs @@ -0,0 +1,173 @@ +// DO NOT EDIT: GENERATED BY Vector2PackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.Vector2PackAttributeTests._1000_27b2 +{ + public class BitPackBehaviour : NetworkBehaviour + { + [Vector2Pack(1000f, 200f, 15, 12)] + [SyncVar] public Vector2 MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction( [Vector2Pack(1000f, 200f, 15, 12)] Vector2 myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [Vector2Pack(1000f, 200f, 15, 12)] + public Vector2 MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [Vector2Pack(1000f, 200f, 15, 12)] + public Vector2 MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Vector2 value = new Vector2(10.3f, 0.2f); + private const float within = 0.2f; + + private static void AssertValue(Vector2 actual) + { + Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); + Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(27)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(27)); + + AssertValue(clientComponent.MyValue); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (27 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"27 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((27 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"27 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(27)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(27)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27b2.cs.meta b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27b2.cs.meta new file mode 100644 index 00000000000..28b21d3b949 --- /dev/null +++ b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27b2.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dafa52920fbbcb74a948431f18d74051 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f.cs b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f.cs new file mode 100644 index 00000000000..9560ea7cc2c --- /dev/null +++ b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f.cs @@ -0,0 +1,173 @@ +// DO NOT EDIT: GENERATED BY Vector2PackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.Vector2PackAttributeTests._1000_27f +{ + public class BitPackBehaviour : NetworkBehaviour + { + [Vector2Pack(1000f, 200f, 0.1f)] + [SyncVar] public Vector2 MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction( [Vector2Pack(1000f, 200f, 0.1f)] Vector2 myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [Vector2Pack(1000f, 200f, 0.1f)] + public Vector2 MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [Vector2Pack(1000f, 200f, 0.1f)] + public Vector2 MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Vector2 value = new Vector2(-10.3f, 0.2f); + private const float within = 0.1f; + + private static void AssertValue(Vector2 actual) + { + Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); + Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(27)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(27)); + + AssertValue(clientComponent.MyValue); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (27 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"27 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((27 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"27 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(27)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(27)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f.cs.meta b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f.cs.meta new file mode 100644 index 00000000000..3b801887b87 --- /dev/null +++ b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 65f22e8001d898c4b8bf1cbb3146f356 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f2.cs b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f2.cs new file mode 100644 index 00000000000..467a7e1b823 --- /dev/null +++ b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f2.cs @@ -0,0 +1,173 @@ +// DO NOT EDIT: GENERATED BY Vector2PackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.Vector2PackAttributeTests._1000_27f2 +{ + public class BitPackBehaviour : NetworkBehaviour + { + [Vector2Pack(1000f, 200f, 0.1f, 0.1f)] + [SyncVar] public Vector2 MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction( [Vector2Pack(1000f, 200f, 0.1f, 0.1f)] Vector2 myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [Vector2Pack(1000f, 200f, 0.1f, 0.1f)] + public Vector2 MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [Vector2Pack(1000f, 200f, 0.1f, 0.1f)] + public Vector2 MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Vector2 value = new Vector2(-10.3f, 0.2f); + private const float within = 0.1f; + + private static void AssertValue(Vector2 actual) + { + Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); + Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(27)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(27)); + + AssertValue(clientComponent.MyValue); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (27 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"27 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((27 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"27 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(27)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(27)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f2.cs.meta b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f2.cs.meta new file mode 100644 index 00000000000..bedff4db580 --- /dev/null +++ b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_27f2.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e72717cde8b02e2409260884fe183589 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_30b.cs b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_30b.cs new file mode 100644 index 00000000000..02d885dc8d2 --- /dev/null +++ b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_30b.cs @@ -0,0 +1,173 @@ +// DO NOT EDIT: GENERATED BY Vector2PackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.Vector2PackAttributeTests._1000_30b +{ + public class BitPackBehaviour : NetworkBehaviour + { + [Vector2Pack(1000f, 200f, 15)] + [SyncVar] public Vector2 MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction( [Vector2Pack(1000f, 200f, 15)] Vector2 myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [Vector2Pack(1000f, 200f, 15)] + public Vector2 MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [Vector2Pack(1000f, 200f, 15)] + public Vector2 MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Vector2 value = new Vector2(10.3f, 0.2f); + private const float within = 0.2f; + + private static void AssertValue(Vector2 actual) + { + Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); + Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(30)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(30)); + + AssertValue(clientComponent.MyValue); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (30 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"30 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((30 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"30 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(30)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(30)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_30b.cs.meta b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_30b.cs.meta new file mode 100644 index 00000000000..448a779e71c --- /dev/null +++ b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_1000_30b.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d36981995f815ab4794b77192fb931fd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18b2.cs b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18b2.cs new file mode 100644 index 00000000000..88950310c4c --- /dev/null +++ b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18b2.cs @@ -0,0 +1,173 @@ +// DO NOT EDIT: GENERATED BY Vector2PackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.Vector2PackAttributeTests._100_18b2 +{ + public class BitPackBehaviour : NetworkBehaviour + { + [Vector2Pack(100f, 20f, 10, 8)] + [SyncVar] public Vector2 MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction( [Vector2Pack(100f, 20f, 10, 8)] Vector2 myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [Vector2Pack(100f, 20f, 10, 8)] + public Vector2 MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [Vector2Pack(100f, 20f, 10, 8)] + public Vector2 MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Vector2 value = new Vector2(-10.3f, 0.2f); + private const float within = 0.2f; + + private static void AssertValue(Vector2 actual) + { + Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); + Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(18)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(18)); + + AssertValue(clientComponent.MyValue); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (18 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"18 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((18 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"18 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(18)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(18)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18b2.cs.meta b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18b2.cs.meta new file mode 100644 index 00000000000..587e5954bd1 --- /dev/null +++ b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18b2.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5258f3f6881332749b165b2287ed73ae +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f.cs b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f.cs new file mode 100644 index 00000000000..e2c0a11d7fb --- /dev/null +++ b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f.cs @@ -0,0 +1,173 @@ +// DO NOT EDIT: GENERATED BY Vector2PackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.Vector2PackAttributeTests._100_18f +{ + public class BitPackBehaviour : NetworkBehaviour + { + [Vector2Pack(100f, 20f, 0.2f)] + [SyncVar] public Vector2 MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction( [Vector2Pack(100f, 20f, 0.2f)] Vector2 myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [Vector2Pack(100f, 20f, 0.2f)] + public Vector2 MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [Vector2Pack(100f, 20f, 0.2f)] + public Vector2 MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Vector2 value = new Vector2(10.3f, 0.2f); + private const float within = 0.2f; + + private static void AssertValue(Vector2 actual) + { + Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); + Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(18)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(18)); + + AssertValue(clientComponent.MyValue); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (18 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"18 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((18 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"18 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(18)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(18)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f.cs.meta b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f.cs.meta new file mode 100644 index 00000000000..a8243b1b398 --- /dev/null +++ b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c82ec5b521d03244fafbcefa2fd4324b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f2.cs b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f2.cs new file mode 100644 index 00000000000..932725f662b --- /dev/null +++ b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f2.cs @@ -0,0 +1,173 @@ +// DO NOT EDIT: GENERATED BY Vector2PackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.Vector2PackAttributeTests._100_18f2 +{ + public class BitPackBehaviour : NetworkBehaviour + { + [Vector2Pack(100f, 20f, 0.2f, 0.2f)] + [SyncVar] public Vector2 MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction( [Vector2Pack(100f, 20f, 0.2f, 0.2f)] Vector2 myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [Vector2Pack(100f, 20f, 0.2f, 0.2f)] + public Vector2 MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [Vector2Pack(100f, 20f, 0.2f, 0.2f)] + public Vector2 MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Vector2 value = new Vector2(10.3f, 0.2f); + private const float within = 0.2f; + + private static void AssertValue(Vector2 actual) + { + Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); + Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(18)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(18)); + + AssertValue(clientComponent.MyValue); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (18 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"18 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((18 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"18 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(18)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(18)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f2.cs.meta b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f2.cs.meta new file mode 100644 index 00000000000..59061f9c783 --- /dev/null +++ b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_18f2.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ea5a473653e52894db56f3876b478586 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_20b.cs b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_20b.cs new file mode 100644 index 00000000000..9ae9a92fab3 --- /dev/null +++ b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_20b.cs @@ -0,0 +1,173 @@ +// DO NOT EDIT: GENERATED BY Vector2PackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.Vector2PackAttributeTests._100_20b +{ + public class BitPackBehaviour : NetworkBehaviour + { + [Vector2Pack(100f, 100f, 10)] + [SyncVar] public Vector2 MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction( [Vector2Pack(100f, 100f, 10)] Vector2 myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [Vector2Pack(100f, 100f, 10)] + public Vector2 MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [Vector2Pack(100f, 100f, 10)] + public Vector2 MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Vector2 value = new Vector2(-10.3f, 0.2f); + private const float within = 0.2f; + + private static void AssertValue(Vector2 actual) + { + Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); + Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(20)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(20)); + + AssertValue(clientComponent.MyValue); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (20 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"20 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((20 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"20 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(20)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(20)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_20b.cs.meta b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_20b.cs.meta new file mode 100644 index 00000000000..6af056437fc --- /dev/null +++ b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_100_20b.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 846700aa697eac74389cd09ec75ba00c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b.cs b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b.cs new file mode 100644 index 00000000000..fe9d0ccd578 --- /dev/null +++ b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b.cs @@ -0,0 +1,173 @@ +// DO NOT EDIT: GENERATED BY Vector2PackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.Vector2PackAttributeTests._200_26b +{ + public class BitPackBehaviour : NetworkBehaviour + { + [Vector2Pack(200f, 200f, 13)] + [SyncVar] public Vector2 MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction( [Vector2Pack(200f, 200f, 13)] Vector2 myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [Vector2Pack(200f, 200f, 13)] + public Vector2 MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [Vector2Pack(200f, 200f, 13)] + public Vector2 MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Vector2 value = new Vector2(10.3f, 0.2f); + private const float within = 0.2f; + + private static void AssertValue(Vector2 actual) + { + Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); + Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(26)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(26)); + + AssertValue(clientComponent.MyValue); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (26 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"26 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((26 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"26 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(26)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(26)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b.cs.meta b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b.cs.meta new file mode 100644 index 00000000000..86ee4309277 --- /dev/null +++ b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bd35effed30320445859623308ab2f77 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b2.cs b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b2.cs new file mode 100644 index 00000000000..ec4917b73a3 --- /dev/null +++ b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b2.cs @@ -0,0 +1,173 @@ +// DO NOT EDIT: GENERATED BY Vector2PackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.Vector2PackAttributeTests._200_26b2 +{ + public class BitPackBehaviour : NetworkBehaviour + { + [Vector2Pack(200f, 200f, 13, 13)] + [SyncVar] public Vector2 MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction( [Vector2Pack(200f, 200f, 13, 13)] Vector2 myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [Vector2Pack(200f, 200f, 13, 13)] + public Vector2 MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [Vector2Pack(200f, 200f, 13, 13)] + public Vector2 MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Vector2 value = new Vector2(10.3f, 0.2f); + private const float within = 0.2f; + + private static void AssertValue(Vector2 actual) + { + Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); + Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(26)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(26)); + + AssertValue(clientComponent.MyValue); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (26 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"26 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((26 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"26 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(26)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(26)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b2.cs.meta b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b2.cs.meta new file mode 100644 index 00000000000..cbca6300060 --- /dev/null +++ b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26b2.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d103769b00e5b2a418d6760f073f6120 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f.cs b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f.cs new file mode 100644 index 00000000000..15bff96fed7 --- /dev/null +++ b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f.cs @@ -0,0 +1,173 @@ +// DO NOT EDIT: GENERATED BY Vector2PackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.Vector2PackAttributeTests._200_26f +{ + public class BitPackBehaviour : NetworkBehaviour + { + [Vector2Pack(200f, 200f, 0.05f)] + [SyncVar] public Vector2 MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction( [Vector2Pack(200f, 200f, 0.05f)] Vector2 myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [Vector2Pack(200f, 200f, 0.05f)] + public Vector2 MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [Vector2Pack(200f, 200f, 0.05f)] + public Vector2 MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Vector2 value = new Vector2(-10.3f, 0.2f); + private const float within = 0.1f; + + private static void AssertValue(Vector2 actual) + { + Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); + Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(26)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(26)); + + AssertValue(clientComponent.MyValue); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (26 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"26 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((26 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"26 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(26)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(26)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f.cs.meta b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f.cs.meta new file mode 100644 index 00000000000..2108d118a6c --- /dev/null +++ b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fa2265763411aae4f8b3cbfc81326029 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f2.cs b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f2.cs new file mode 100644 index 00000000000..030ebc8c98a --- /dev/null +++ b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f2.cs @@ -0,0 +1,173 @@ +// DO NOT EDIT: GENERATED BY Vector2PackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.Vector2PackAttributeTests._200_26f2 +{ + public class BitPackBehaviour : NetworkBehaviour + { + [Vector2Pack(200f, 200f, 0.05f, 0.05f)] + [SyncVar] public Vector2 MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction( [Vector2Pack(200f, 200f, 0.05f, 0.05f)] Vector2 myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [Vector2Pack(200f, 200f, 0.05f, 0.05f)] + public Vector2 MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [Vector2Pack(200f, 200f, 0.05f, 0.05f)] + public Vector2 MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Vector2 value = new Vector2(-10.3f, 0.2f); + private const float within = 0.1f; + + private static void AssertValue(Vector2 actual) + { + Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); + Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(26)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(26)); + + AssertValue(clientComponent.MyValue); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (26 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"26 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((26 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"26 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(26)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(26)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f2.cs.meta b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f2.cs.meta new file mode 100644 index 00000000000..9bf1ac58253 --- /dev/null +++ b/Assets/Tests/Generated/Vector2PackTests/Vector2PackBehaviour_200_26f2.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0703f8afdcf184240a74c61c677fe45b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector3PackTests.meta b/Assets/Tests/Generated/Vector3PackTests.meta new file mode 100644 index 00000000000..d5120f0a329 --- /dev/null +++ b/Assets/Tests/Generated/Vector3PackTests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a569ce7d0369c2e428e992a41ec3b569 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42b3.cs b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42b3.cs new file mode 100644 index 00000000000..9afdae87f89 --- /dev/null +++ b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42b3.cs @@ -0,0 +1,176 @@ +// DO NOT EDIT: GENERATED BY Vector3PackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.Vector3PackAttributeTests._1000_42b3 +{ + public class BitPackBehaviour : NetworkBehaviour + { + [Vector3Pack(1000f, 200f, 1000f, 15, 12, 15)] + [SyncVar] public Vector3 MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([Vector3Pack(1000f, 200f, 1000f, 15, 12, 15)] Vector3 myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [Vector3Pack(1000f, 200f, 1000f, 15, 12, 15)] + public Vector3 MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [Vector3Pack(1000f, 200f, 1000f, 15, 12, 15)] + public Vector3 MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Vector3 value = new Vector3(10.3f, 0.2f, 20f); + private const float within = 0.2f; + + private static void AssertValue(Vector3 actual) + { + Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); + Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); + Assert.That(actual.z, Is.EqualTo(value.z).Within(within)); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(42)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(42)); + + Assert.That(clientComponent.MyValue.x, Is.EqualTo(value.x).Within(within)); + Assert.That(clientComponent.MyValue.y, Is.EqualTo(value.y).Within(within)); + Assert.That(clientComponent.MyValue.z, Is.EqualTo(value.z).Within(within)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (42 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"42 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((42 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"42 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(42)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(42)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42b3.cs.meta b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42b3.cs.meta new file mode 100644 index 00000000000..516650fa069 --- /dev/null +++ b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42b3.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4499369b48aebcd4d9e9f49521ab1c89 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f.cs b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f.cs new file mode 100644 index 00000000000..94e08a30255 --- /dev/null +++ b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f.cs @@ -0,0 +1,176 @@ +// DO NOT EDIT: GENERATED BY Vector3PackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.Vector3PackAttributeTests._1000_42f +{ + public class BitPackBehaviour : NetworkBehaviour + { + [Vector3Pack(1000f, 200f, 1000f, 0.1f)] + [SyncVar] public Vector3 MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([Vector3Pack(1000f, 200f, 1000f, 0.1f)] Vector3 myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [Vector3Pack(1000f, 200f, 1000f, 0.1f)] + public Vector3 MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [Vector3Pack(1000f, 200f, 1000f, 0.1f)] + public Vector3 MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Vector3 value = new Vector3(-10.3f, 0.2f, 20f); + private const float within = 0.1f; + + private static void AssertValue(Vector3 actual) + { + Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); + Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); + Assert.That(actual.z, Is.EqualTo(value.z).Within(within)); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(42)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(42)); + + Assert.That(clientComponent.MyValue.x, Is.EqualTo(value.x).Within(within)); + Assert.That(clientComponent.MyValue.y, Is.EqualTo(value.y).Within(within)); + Assert.That(clientComponent.MyValue.z, Is.EqualTo(value.z).Within(within)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (42 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"42 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((42 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"42 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(42)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(42)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f.cs.meta b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f.cs.meta new file mode 100644 index 00000000000..a346db8c561 --- /dev/null +++ b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 61a4dff1c7f48fa4abca392cea2cf26c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f3.cs b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f3.cs new file mode 100644 index 00000000000..a34ed1b0a6f --- /dev/null +++ b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f3.cs @@ -0,0 +1,176 @@ +// DO NOT EDIT: GENERATED BY Vector3PackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.Vector3PackAttributeTests._1000_42f3 +{ + public class BitPackBehaviour : NetworkBehaviour + { + [Vector3Pack(1000f, 200f, 1000f, 0.1f, 0.1f, 0.1f)] + [SyncVar] public Vector3 MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([Vector3Pack(1000f, 200f, 1000f, 0.1f, 0.1f, 0.1f)] Vector3 myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [Vector3Pack(1000f, 200f, 1000f, 0.1f, 0.1f, 0.1f)] + public Vector3 MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [Vector3Pack(1000f, 200f, 1000f, 0.1f, 0.1f, 0.1f)] + public Vector3 MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Vector3 value = new Vector3(-10.3f, 0.2f, 20f); + private const float within = 0.1f; + + private static void AssertValue(Vector3 actual) + { + Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); + Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); + Assert.That(actual.z, Is.EqualTo(value.z).Within(within)); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(42)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(42)); + + Assert.That(clientComponent.MyValue.x, Is.EqualTo(value.x).Within(within)); + Assert.That(clientComponent.MyValue.y, Is.EqualTo(value.y).Within(within)); + Assert.That(clientComponent.MyValue.z, Is.EqualTo(value.z).Within(within)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (42 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"42 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((42 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"42 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(42)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(42)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f3.cs.meta b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f3.cs.meta new file mode 100644 index 00000000000..718c8ba4270 --- /dev/null +++ b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_42f3.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e1eaae3b2239bb04288ae1054b457c3e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_45b.cs b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_45b.cs new file mode 100644 index 00000000000..b3edd9ef33f --- /dev/null +++ b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_45b.cs @@ -0,0 +1,176 @@ +// DO NOT EDIT: GENERATED BY Vector3PackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.Vector3PackAttributeTests._1000_45b +{ + public class BitPackBehaviour : NetworkBehaviour + { + [Vector3Pack(1000f, 200f, 1000f, 15)] + [SyncVar] public Vector3 MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([Vector3Pack(1000f, 200f, 1000f, 15)] Vector3 myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [Vector3Pack(1000f, 200f, 1000f, 15)] + public Vector3 MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [Vector3Pack(1000f, 200f, 1000f, 15)] + public Vector3 MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Vector3 value = new Vector3(10.3f, 0.2f, 20f); + private const float within = 0.2f; + + private static void AssertValue(Vector3 actual) + { + Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); + Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); + Assert.That(actual.z, Is.EqualTo(value.z).Within(within)); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(45)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(45)); + + Assert.That(clientComponent.MyValue.x, Is.EqualTo(value.x).Within(within)); + Assert.That(clientComponent.MyValue.y, Is.EqualTo(value.y).Within(within)); + Assert.That(clientComponent.MyValue.z, Is.EqualTo(value.z).Within(within)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (45 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"45 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((45 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"45 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(45)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(45)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_45b.cs.meta b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_45b.cs.meta new file mode 100644 index 00000000000..1f55cbec5f7 --- /dev/null +++ b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_1000_45b.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 82e8e82c3e3d5aa40b0f8b17009f95c8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28b3.cs b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28b3.cs new file mode 100644 index 00000000000..9893501b463 --- /dev/null +++ b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28b3.cs @@ -0,0 +1,176 @@ +// DO NOT EDIT: GENERATED BY Vector3PackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.Vector3PackAttributeTests._100_28b3 +{ + public class BitPackBehaviour : NetworkBehaviour + { + [Vector3Pack(100f, 20f, 100f, 10, 8, 10)] + [SyncVar] public Vector3 MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([Vector3Pack(100f, 20f, 100f, 10, 8, 10)] Vector3 myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [Vector3Pack(100f, 20f, 100f, 10, 8, 10)] + public Vector3 MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [Vector3Pack(100f, 20f, 100f, 10, 8, 10)] + public Vector3 MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Vector3 value = new Vector3(-10.3f, 0.2f, 20f); + private const float within = 0.2f; + + private static void AssertValue(Vector3 actual) + { + Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); + Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); + Assert.That(actual.z, Is.EqualTo(value.z).Within(within)); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(28)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(28)); + + Assert.That(clientComponent.MyValue.x, Is.EqualTo(value.x).Within(within)); + Assert.That(clientComponent.MyValue.y, Is.EqualTo(value.y).Within(within)); + Assert.That(clientComponent.MyValue.z, Is.EqualTo(value.z).Within(within)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (28 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"28 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((28 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"28 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(28)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(28)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28b3.cs.meta b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28b3.cs.meta new file mode 100644 index 00000000000..ad8ffff1d92 --- /dev/null +++ b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28b3.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: df73554e4d468d44fb5d363a7871f5b5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f.cs b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f.cs new file mode 100644 index 00000000000..fdeef138f25 --- /dev/null +++ b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f.cs @@ -0,0 +1,176 @@ +// DO NOT EDIT: GENERATED BY Vector3PackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.Vector3PackAttributeTests._100_28f +{ + public class BitPackBehaviour : NetworkBehaviour + { + [Vector3Pack(100f, 20f, 100f, 0.2f)] + [SyncVar] public Vector3 MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([Vector3Pack(100f, 20f, 100f, 0.2f)] Vector3 myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [Vector3Pack(100f, 20f, 100f, 0.2f)] + public Vector3 MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [Vector3Pack(100f, 20f, 100f, 0.2f)] + public Vector3 MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Vector3 value = new Vector3(10.3f, 0.2f, 20f); + private const float within = 0.2f; + + private static void AssertValue(Vector3 actual) + { + Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); + Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); + Assert.That(actual.z, Is.EqualTo(value.z).Within(within)); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(28)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(28)); + + Assert.That(clientComponent.MyValue.x, Is.EqualTo(value.x).Within(within)); + Assert.That(clientComponent.MyValue.y, Is.EqualTo(value.y).Within(within)); + Assert.That(clientComponent.MyValue.z, Is.EqualTo(value.z).Within(within)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (28 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"28 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((28 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"28 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(28)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(28)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f.cs.meta b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f.cs.meta new file mode 100644 index 00000000000..e79c15a5926 --- /dev/null +++ b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 201a148d2fce2454fada358a9f3572c4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f3.cs b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f3.cs new file mode 100644 index 00000000000..60dcea5ced7 --- /dev/null +++ b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f3.cs @@ -0,0 +1,176 @@ +// DO NOT EDIT: GENERATED BY Vector3PackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.Vector3PackAttributeTests._100_28f3 +{ + public class BitPackBehaviour : NetworkBehaviour + { + [Vector3Pack(100f, 20f, 100f, 0.2f, 0.2f, 0.2f)] + [SyncVar] public Vector3 MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([Vector3Pack(100f, 20f, 100f, 0.2f, 0.2f, 0.2f)] Vector3 myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [Vector3Pack(100f, 20f, 100f, 0.2f, 0.2f, 0.2f)] + public Vector3 MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [Vector3Pack(100f, 20f, 100f, 0.2f, 0.2f, 0.2f)] + public Vector3 MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Vector3 value = new Vector3(10.3f, 0.2f, 20f); + private const float within = 0.2f; + + private static void AssertValue(Vector3 actual) + { + Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); + Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); + Assert.That(actual.z, Is.EqualTo(value.z).Within(within)); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(28)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(28)); + + Assert.That(clientComponent.MyValue.x, Is.EqualTo(value.x).Within(within)); + Assert.That(clientComponent.MyValue.y, Is.EqualTo(value.y).Within(within)); + Assert.That(clientComponent.MyValue.z, Is.EqualTo(value.z).Within(within)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (28 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"28 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((28 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"28 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(28)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(28)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f3.cs.meta b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f3.cs.meta new file mode 100644 index 00000000000..831a4f25261 --- /dev/null +++ b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_28f3.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: adef600428bbf1544b685f42f50180a9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_30b.cs b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_30b.cs new file mode 100644 index 00000000000..3e1f534846b --- /dev/null +++ b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_30b.cs @@ -0,0 +1,176 @@ +// DO NOT EDIT: GENERATED BY Vector3PackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.Vector3PackAttributeTests._100_30b +{ + public class BitPackBehaviour : NetworkBehaviour + { + [Vector3Pack(100f, 100f, 100f, 10)] + [SyncVar] public Vector3 MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([Vector3Pack(100f, 100f, 100f, 10)] Vector3 myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [Vector3Pack(100f, 100f, 100f, 10)] + public Vector3 MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [Vector3Pack(100f, 100f, 100f, 10)] + public Vector3 MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Vector3 value = new Vector3(-10.3f, 0.2f, 20f); + private const float within = 0.2f; + + private static void AssertValue(Vector3 actual) + { + Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); + Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); + Assert.That(actual.z, Is.EqualTo(value.z).Within(within)); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(30)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(30)); + + Assert.That(clientComponent.MyValue.x, Is.EqualTo(value.x).Within(within)); + Assert.That(clientComponent.MyValue.y, Is.EqualTo(value.y).Within(within)); + Assert.That(clientComponent.MyValue.z, Is.EqualTo(value.z).Within(within)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (30 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"30 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((30 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"30 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(30)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(30)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_30b.cs.meta b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_30b.cs.meta new file mode 100644 index 00000000000..bc5b62451e5 --- /dev/null +++ b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_100_30b.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4efe76aa2210be54385ba6d4793bed9a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b.cs b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b.cs new file mode 100644 index 00000000000..094828e3de4 --- /dev/null +++ b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b.cs @@ -0,0 +1,176 @@ +// DO NOT EDIT: GENERATED BY Vector3PackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.Vector3PackAttributeTests._200_39b +{ + public class BitPackBehaviour : NetworkBehaviour + { + [Vector3Pack(200f, 200f, 200f, 13)] + [SyncVar] public Vector3 MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([Vector3Pack(200f, 200f, 200f, 13)] Vector3 myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [Vector3Pack(200f, 200f, 200f, 13)] + public Vector3 MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [Vector3Pack(200f, 200f, 200f, 13)] + public Vector3 MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Vector3 value = new Vector3(10.3f, 0.2f, 20f); + private const float within = 0.2f; + + private static void AssertValue(Vector3 actual) + { + Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); + Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); + Assert.That(actual.z, Is.EqualTo(value.z).Within(within)); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(39)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(39)); + + Assert.That(clientComponent.MyValue.x, Is.EqualTo(value.x).Within(within)); + Assert.That(clientComponent.MyValue.y, Is.EqualTo(value.y).Within(within)); + Assert.That(clientComponent.MyValue.z, Is.EqualTo(value.z).Within(within)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (39 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"39 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((39 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"39 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(39)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(39)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b.cs.meta b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b.cs.meta new file mode 100644 index 00000000000..dcfbbe6b70c --- /dev/null +++ b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 769801aa4625b6741b04b8cfea643166 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b3.cs b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b3.cs new file mode 100644 index 00000000000..7d568739a61 --- /dev/null +++ b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b3.cs @@ -0,0 +1,176 @@ +// DO NOT EDIT: GENERATED BY Vector3PackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.Vector3PackAttributeTests._200_39b3 +{ + public class BitPackBehaviour : NetworkBehaviour + { + [Vector3Pack(200f, 200f, 200f, 13, 13, 13)] + [SyncVar] public Vector3 MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([Vector3Pack(200f, 200f, 200f, 13, 13, 13)] Vector3 myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [Vector3Pack(200f, 200f, 200f, 13, 13, 13)] + public Vector3 MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [Vector3Pack(200f, 200f, 200f, 13, 13, 13)] + public Vector3 MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Vector3 value = new Vector3(10.3f, 0.2f, 20f); + private const float within = 0.2f; + + private static void AssertValue(Vector3 actual) + { + Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); + Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); + Assert.That(actual.z, Is.EqualTo(value.z).Within(within)); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(39)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(39)); + + Assert.That(clientComponent.MyValue.x, Is.EqualTo(value.x).Within(within)); + Assert.That(clientComponent.MyValue.y, Is.EqualTo(value.y).Within(within)); + Assert.That(clientComponent.MyValue.z, Is.EqualTo(value.z).Within(within)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (39 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"39 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((39 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"39 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(39)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(39)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b3.cs.meta b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b3.cs.meta new file mode 100644 index 00000000000..3274ebbae55 --- /dev/null +++ b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39b3.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1befa91894c23e34fa364fadf7d44ab9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f.cs b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f.cs new file mode 100644 index 00000000000..9a7e363ffe1 --- /dev/null +++ b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f.cs @@ -0,0 +1,176 @@ +// DO NOT EDIT: GENERATED BY Vector3PackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.Vector3PackAttributeTests._200_39f +{ + public class BitPackBehaviour : NetworkBehaviour + { + [Vector3Pack(200f, 200f, 200f, 0.05f)] + [SyncVar] public Vector3 MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([Vector3Pack(200f, 200f, 200f, 0.05f)] Vector3 myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [Vector3Pack(200f, 200f, 200f, 0.05f)] + public Vector3 MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [Vector3Pack(200f, 200f, 200f, 0.05f)] + public Vector3 MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Vector3 value = new Vector3(-10.3f, 0.2f, -20f); + private const float within = 0.1f; + + private static void AssertValue(Vector3 actual) + { + Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); + Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); + Assert.That(actual.z, Is.EqualTo(value.z).Within(within)); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(39)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(39)); + + Assert.That(clientComponent.MyValue.x, Is.EqualTo(value.x).Within(within)); + Assert.That(clientComponent.MyValue.y, Is.EqualTo(value.y).Within(within)); + Assert.That(clientComponent.MyValue.z, Is.EqualTo(value.z).Within(within)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (39 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"39 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((39 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"39 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(39)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(39)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f.cs.meta b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f.cs.meta new file mode 100644 index 00000000000..63cc9e7c093 --- /dev/null +++ b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 96c1c96f45ce08c48afcb836421a3fb8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f3.cs b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f3.cs new file mode 100644 index 00000000000..6196674637e --- /dev/null +++ b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f3.cs @@ -0,0 +1,176 @@ +// DO NOT EDIT: GENERATED BY Vector3PackTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.Vector3PackAttributeTests._200_39f3 +{ + public class BitPackBehaviour : NetworkBehaviour + { + [Vector3Pack(200f, 200f, 200f, 0.05f, 0.05f, 0.05f)] + [SyncVar] public Vector3 MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([Vector3Pack(200f, 200f, 200f, 0.05f, 0.05f, 0.05f)] Vector3 myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [Vector3Pack(200f, 200f, 200f, 0.05f, 0.05f, 0.05f)] + public Vector3 MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [Vector3Pack(200f, 200f, 200f, 0.05f, 0.05f, 0.05f)] + public Vector3 MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private static readonly Vector3 value = new Vector3(-10.3f, 0.2f, -20f); + private const float within = 0.1f; + + private static void AssertValue(Vector3 actual) + { + Assert.That(actual.x, Is.EqualTo(value.x).Within(within)); + Assert.That(actual.y, Is.EqualTo(value.y).Within(within)); + Assert.That(actual.z, Is.EqualTo(value.z).Within(within)); + } + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(39)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(39)); + + Assert.That(clientComponent.MyValue.x, Is.EqualTo(value.x).Within(within)); + Assert.That(clientComponent.MyValue.y, Is.EqualTo(value.y).Within(within)); + Assert.That(clientComponent.MyValue.z, Is.EqualTo(value.z).Within(within)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + AssertValue(v); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (39 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"39 bits is %%PAYLOAD_SIZE%% bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((39 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"39 bits is {expectedPayLoadSize - 2} bytes in payload"); + AssertValue(outMessage.MyValue); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(39)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(39)); + + AssertValue(outStruct.MyValue); + } + } + } + } +} diff --git a/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f3.cs.meta b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f3.cs.meta new file mode 100644 index 00000000000..a545c7db462 --- /dev/null +++ b/Assets/Tests/Generated/Vector3PackTests/Vector3PackBehaviour_200_39f3.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f449b59b1825478419f46284820806c3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/ZigZagTests.meta b/Assets/Tests/Generated/ZigZagTests.meta new file mode 100644 index 00000000000..7405489ec7f --- /dev/null +++ b/Assets/Tests/Generated/ZigZagTests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 21a2a3c1512fe0b42af86e8b54ba7a33 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum2_4_negative.cs b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum2_4_negative.cs new file mode 100644 index 00000000000..f0ee0800189 --- /dev/null +++ b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum2_4_negative.cs @@ -0,0 +1,173 @@ +// DO NOT EDIT: GENERATED BY ZigZagTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.ZigZagAttributeTests.MyEnum2_4_negative +{ + [System.Serializable] + public enum MyEnum2 + { + Negative = -1, + Zero = 0, + Positive = 1, + } + public class BitPackBehaviour : NetworkBehaviour + { + [BitCount(4), ZigZagEncode] + [SyncVar] public MyEnum2 MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCount(4), ZigZagEncode] MyEnum2 myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCount(4), ZigZagEncode] + public MyEnum2 MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCount(4), ZigZagEncode] + public MyEnum2 MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const MyEnum2 value = (MyEnum2)(-1); + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(4)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(4)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (4 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"4 bits is 1 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((4 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"4 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(4)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(4)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum2_4_negative.cs.meta b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum2_4_negative.cs.meta new file mode 100644 index 00000000000..a43e72c5c8a --- /dev/null +++ b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum2_4_negative.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 93d454d332b06d34589a44dc7704fa83 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum_4.cs b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum_4.cs new file mode 100644 index 00000000000..d3af706e0d2 --- /dev/null +++ b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum_4.cs @@ -0,0 +1,173 @@ +// DO NOT EDIT: GENERATED BY ZigZagTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.ZigZagAttributeTests.MyEnum_4 +{ + [System.Serializable] + public enum MyEnum + { + Negative = -1, + Zero = 0, + Positive = 1, + } + public class BitPackBehaviour : NetworkBehaviour + { + [BitCount(4), ZigZagEncode] + [SyncVar] public MyEnum MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCount(4), ZigZagEncode] MyEnum myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCount(4), ZigZagEncode] + public MyEnum MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCount(4), ZigZagEncode] + public MyEnum MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const MyEnum value = (MyEnum)1; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(4)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(4)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (4 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"4 bits is 1 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((4 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"4 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(4)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(4)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum_4.cs.meta b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum_4.cs.meta new file mode 100644 index 00000000000..0c652b659fb --- /dev/null +++ b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_MyEnum_4.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8352249ab3b565e4daa89d65f9afb9ca +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10.cs b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10.cs new file mode 100644 index 00000000000..69fdc637bd7 --- /dev/null +++ b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY ZigZagTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.ZigZagAttributeTests.int_10 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [BitCount(10), ZigZagEncode] + [SyncVar] public int MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCount(10), ZigZagEncode] int myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCount(10), ZigZagEncode] + public int MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCount(10), ZigZagEncode] + public int MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const int value = 100; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(10)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(10)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (10 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is 2 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((10 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(10)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(10)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10.cs.meta b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10.cs.meta new file mode 100644 index 00000000000..c31d6b6ab34 --- /dev/null +++ b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6acef4578f12518468496121bf1df8de +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10_negative.cs b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10_negative.cs new file mode 100644 index 00000000000..e2badad8b2a --- /dev/null +++ b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10_negative.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY ZigZagTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.ZigZagAttributeTests.int_10_negative +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [BitCount(10), ZigZagEncode] + [SyncVar] public int MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCount(10), ZigZagEncode] int myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCount(10), ZigZagEncode] + public int MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCount(10), ZigZagEncode] + public int MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const int value = -25; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(10)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(10)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (10 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is 2 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((10 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(10)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(10)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10_negative.cs.meta b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10_negative.cs.meta new file mode 100644 index 00000000000..9176e8f0b2c --- /dev/null +++ b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_int_10_negative.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 42b348f9e44bab244878c17bfec84701 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10.cs b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10.cs new file mode 100644 index 00000000000..6e20b0e4cd1 --- /dev/null +++ b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY ZigZagTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.ZigZagAttributeTests.long_10 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [BitCount(10), ZigZagEncode] + [SyncVar] public long MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCount(10), ZigZagEncode] long myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCount(10), ZigZagEncode] + public long MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCount(10), ZigZagEncode] + public long MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const long value = 14; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(10)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(10)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (10 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is 2 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((10 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(10)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(10)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10.cs.meta b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10.cs.meta new file mode 100644 index 00000000000..b5d11ac3119 --- /dev/null +++ b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 172d661bb687b6d49a7ab94b3417bafc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10_negative.cs b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10_negative.cs new file mode 100644 index 00000000000..5a58b4b36c3 --- /dev/null +++ b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10_negative.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY ZigZagTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.ZigZagAttributeTests.long_10_negative +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [BitCount(10), ZigZagEncode] + [SyncVar] public long MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCount(10), ZigZagEncode] long myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCount(10), ZigZagEncode] + public long MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCount(10), ZigZagEncode] + public long MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const long value = -30; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(10)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(10)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (10 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is 2 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((10 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(10)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(10)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10_negative.cs.meta b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10_negative.cs.meta new file mode 100644 index 00000000000..9ca75464db6 --- /dev/null +++ b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_long_10_negative.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 43ae8061a6b6d9442a52e73c698b2cd4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10.cs b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10.cs new file mode 100644 index 00000000000..e20b281c6a0 --- /dev/null +++ b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY ZigZagTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.ZigZagAttributeTests.short_10 +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [BitCount(10), ZigZagEncode] + [SyncVar] public short MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCount(10), ZigZagEncode] short myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCount(10), ZigZagEncode] + public short MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCount(10), ZigZagEncode] + public short MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const short value = 15; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(10)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(10)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (10 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is 2 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((10 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(10)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(10)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10.cs.meta b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10.cs.meta new file mode 100644 index 00000000000..a956d669b9c --- /dev/null +++ b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c026d578164f23c41a2419a1d42e07cc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10_negative.cs b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10_negative.cs new file mode 100644 index 00000000000..98c438e11c2 --- /dev/null +++ b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10_negative.cs @@ -0,0 +1,167 @@ +// DO NOT EDIT: GENERATED BY ZigZagTestGenerator.cs + +using System; +using System.Collections; +using Mirage.RemoteCalls; +using Mirage.Serialization; +using Mirage.Tests.Runtime.ClientServer; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Mirage.Tests.Runtime.Generated.ZigZagAttributeTests.short_10_negative +{ + + public class BitPackBehaviour : NetworkBehaviour + { + [BitCount(10), ZigZagEncode] + [SyncVar] public short MyValue { get; set; } + + public event Action onRpc; + + [ClientRpc] + public void RpcSomeFunction([BitCount(10), ZigZagEncode] short myParam) + { + onRpc?.Invoke(myParam); + } + + // Use BitPackStruct in rpc so it has writer generated + [ClientRpc] + public void RpcOtherFunction(BitPackStruct myParam) + { + // nothing + } + } + + [NetworkMessage] + public struct BitPackMessage + { + [BitCount(10), ZigZagEncode] + public short MyValue; + } + + [Serializable] + public struct BitPackStruct + { + [BitCount(10), ZigZagEncode] + public short MyValue; + } + + public class BitPackTest : ClientServerSetup + { + private const short value = -20; + + [Test] + public void SyncVarIsBitPacked() + { + serverComponent.MyValue = value; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + serverComponent.SerializeSyncVars(writer, true); + + Assert.That(writer.BitPosition, Is.EqualTo(10)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + clientComponent.DeserializeSyncVars(reader, true); + Assert.That(reader.BitPosition, Is.EqualTo(10)); + + Assert.That(clientComponent.MyValue, Is.EqualTo(value)); + } + } + } + + [UnityTest] + public IEnumerator RpcIsBitPacked() + { + int called = 0; + clientComponent.onRpc += (v) => + { + called++; + Assert.That(v, Is.EqualTo(value)); + }; + + client.MessageHandler.UnregisterHandler(); + int payloadSize = 0; + client.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + payloadSize = msg.Payload.Count; + clientObjectManager._rpcHandler.OnRpcMessage(player, msg); + }); + + serverComponent.RpcSomeFunction(value); + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + + // this will round up to nearest 8 + int expectedPayLoadSize = (10 + 7) / 8; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is 2 bytes in payload"); + } + + [UnityTest] + public IEnumerator StructIsBitPacked() + { + var inMessage = new BitPackMessage + { + MyValue = value, + }; + + int payloadSize = 0; + int called = 0; + BitPackMessage outMessage = default; + server.MessageHandler.RegisterHandler((player, msg) => + { + // store value in variable because assert will throw and be catch by message wrapper + called++; + outMessage = msg; + }); + Action diagAction = (info) => + { + if (info.message is BitPackMessage) + { + payloadSize = info.bytes; + } + }; + + NetworkDiagnostics.OutMessageEvent += diagAction; + client.Player.Send(inMessage); + NetworkDiagnostics.OutMessageEvent -= diagAction; + yield return null; + yield return null; + Assert.That(called, Is.EqualTo(1)); + // this will round up to nearest 8 + // +2 for message header + int expectedPayLoadSize = ((10 + 7) / 8) + 2; + Assert.That(payloadSize, Is.EqualTo(expectedPayLoadSize), $"10 bits is {expectedPayLoadSize - 2} bytes in payload"); + Assert.That(outMessage, Is.EqualTo(inMessage)); + } + + [Test] + public void MessageIsBitPacked() + { + var inStruct = new BitPackStruct + { + MyValue = value, + }; + + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + // generic write, uses generated function that should include bitPacking + writer.Write(inStruct); + + Assert.That(writer.BitPosition, Is.EqualTo(10)); + + using (PooledNetworkReader reader = NetworkReaderPool.GetReader(writer.ToArraySegment(), null)) + { + var outStruct = reader.Read(); + Assert.That(reader.BitPosition, Is.EqualTo(10)); + + Assert.That(outStruct, Is.EqualTo(inStruct)); + } + } + } + } +} diff --git a/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10_negative.cs.meta b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10_negative.cs.meta new file mode 100644 index 00000000000..d1146b071b9 --- /dev/null +++ b/Assets/Tests/Generated/ZigZagTests/ZigZagBehaviour_short_10_negative.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 08c0171828b97284c9b3caf4b2e128eb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From b172a96aad54e1824bf98c0ad8933966fe376054 Mon Sep 17 00:00:00 2001 From: James Frowen Date: Sat, 30 May 2026 23:58:04 +0100 Subject: [PATCH 09/41] fix: fixing compile errors --- .../Weaver/Processors/PropertySiteProcessor.cs.meta | 11 ----------- Assets/Mirage/Weaver/Processors/SyncVarProcessor.cs | 4 +--- 2 files changed, 1 insertion(+), 14 deletions(-) delete mode 100644 Assets/Mirage/Weaver/Processors/PropertySiteProcessor.cs.meta diff --git a/Assets/Mirage/Weaver/Processors/PropertySiteProcessor.cs.meta b/Assets/Mirage/Weaver/Processors/PropertySiteProcessor.cs.meta deleted file mode 100644 index e8c2500adc3..00000000000 --- a/Assets/Mirage/Weaver/Processors/PropertySiteProcessor.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: d48f1ab125e9940a995603796bccc59e -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Mirage/Weaver/Processors/SyncVarProcessor.cs b/Assets/Mirage/Weaver/Processors/SyncVarProcessor.cs index 44eda8f7d10..412db0a6cee 100644 --- a/Assets/Mirage/Weaver/Processors/SyncVarProcessor.cs +++ b/Assets/Mirage/Weaver/Processors/SyncVarProcessor.cs @@ -1,13 +1,11 @@ using System; +using System.Linq; using Mirage.CodeGen; using Mirage.Weaver.NetworkBehaviours; using Mirage.Weaver.Serialization; using Mirage.Weaver.SyncVars; using Mono.Cecil; using Mono.Cecil.Cil; -using FieldAttributes = Mono.Cecil.FieldAttributes; -using MethodAttributes = Mono.Cecil.MethodAttributes; -using PropertyAttributes = Mono.Cecil.PropertyAttributes; namespace Mirage.Weaver { From 158d358c2100d26a2f8b625b965a65428ef13ebe Mon Sep 17 00:00:00 2001 From: James Frowen Date: Sat, 30 May 2026 23:58:24 +0100 Subject: [PATCH 10/41] fix: attribute --- Assets/Mirage/Runtime/Serialization/WeaverAttributes.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Mirage/Runtime/Serialization/WeaverAttributes.cs b/Assets/Mirage/Runtime/Serialization/WeaverAttributes.cs index 7488ffe7ffd..701415575a0 100644 --- a/Assets/Mirage/Runtime/Serialization/WeaverAttributes.cs +++ b/Assets/Mirage/Runtime/Serialization/WeaverAttributes.cs @@ -8,7 +8,7 @@ namespace Mirage.Serialization /// /// Tells Weaver to ignore a field or Method /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] public sealed class WeaverIgnoreAttribute : Attribute { } /// From f01276a38a9e329a15063e8781aa604f438ab338 Mon Sep 17 00:00:00 2001 From: James Frowen Date: Sat, 30 May 2026 23:58:36 +0100 Subject: [PATCH 11/41] updating components --- Assets/Mirage/Components/ReadyCheck.cs | 2 +- Assets/Mirage/Components/SyncObjectActive.cs | 2 +- Assets/Mirage/Components/Visibility/NetworkMatchChecker.cs | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Assets/Mirage/Components/ReadyCheck.cs b/Assets/Mirage/Components/ReadyCheck.cs index 6e93cacb670..30e90959294 100644 --- a/Assets/Mirage/Components/ReadyCheck.cs +++ b/Assets/Mirage/Components/ReadyCheck.cs @@ -13,7 +13,7 @@ public class ReadyCheck : NetworkBehaviour public event Action OnReadyChanged; [SyncVar(hook = nameof(OnReadyChanged), invokeHookOnServer = true, invokeHookOnOwner = true)] - private bool _isReady; + private bool _isReady { get; set; } public bool IsReady => _isReady; diff --git a/Assets/Mirage/Components/SyncObjectActive.cs b/Assets/Mirage/Components/SyncObjectActive.cs index f188d866227..b3517fe8509 100644 --- a/Assets/Mirage/Components/SyncObjectActive.cs +++ b/Assets/Mirage/Components/SyncObjectActive.cs @@ -4,7 +4,7 @@ public class SyncObjectActive : NetworkBehaviour { // todo update this to work with "invoke on sender" when syncvar has that setting [SyncVar(hook = nameof(ActiveChanged), invokeHookOnServer = true)] - private bool _active; + private bool _active { get; set; } private void ActiveChanged(bool nowActive) { diff --git a/Assets/Mirage/Components/Visibility/NetworkMatchChecker.cs b/Assets/Mirage/Components/Visibility/NetworkMatchChecker.cs index 3b833f1df3f..2b46447414e 100644 --- a/Assets/Mirage/Components/Visibility/NetworkMatchChecker.cs +++ b/Assets/Mirage/Components/Visibility/NetworkMatchChecker.cs @@ -18,9 +18,8 @@ public class NetworkMatchChecker : NetworkVisibility private static readonly Dictionary> matchPlayers = new Dictionary>(); private Guid currentMatch = Guid.Empty; - [Header("Diagnostics")] [SyncVar] - public string currentMatchDebug; + public string currentMatchDebug { get; set; } /// /// Set this to the same value on all networked objects that belong to a given match From 5f1a3da2b5ba6fc5b5dd494d859df9bcf636b857 Mon Sep 17 00:00:00 2001 From: James Frowen Date: Sun, 31 May 2026 10:08:52 +0100 Subject: [PATCH 12/41] fix: support properties in attribute checks, copy packer attributes, and update test signatures --- .../Weaver/Processors/AttributeProcessor.cs | 11 ++++++++++ .../Weaver/Processors/SyncVarProcessor.cs | 2 +- .../Weaver/GeneralTests~/RecursionCount.cs | 2 +- .../NetworkBehaviourAbstractBaseValid.cs | 4 ++-- .../NetworkBehaviourValid.cs | 2 +- Assets/Tests/Weaver/SyncVarHookTests.cs | 20 +++++++++---------- Assets/Tests/Weaver/SyncVarTests.cs | 10 +++++----- 7 files changed, 31 insertions(+), 20 deletions(-) diff --git a/Assets/Mirage/Weaver/Processors/AttributeProcessor.cs b/Assets/Mirage/Weaver/Processors/AttributeProcessor.cs index eb1afe95329..7bd86f96e46 100644 --- a/Assets/Mirage/Weaver/Processors/AttributeProcessor.cs +++ b/Assets/Mirage/Weaver/Processors/AttributeProcessor.cs @@ -54,6 +54,11 @@ private void ProcessType(FoundType foundType) { ProcessFields(fd, foundType); } + + foreach (var pd in foundType.TypeDefinition.Properties) + { + ProcessProperties(pd, foundType); + } } } @@ -74,6 +79,12 @@ private void ProcessFields(FieldDefinition fd, FoundType foundType) } } + private void ProcessProperties(PropertyDefinition pd, FoundType foundType) + { + if (pd.HasCustomAttribute()) + logger.Error($"SyncVar {pd.Name} must be inside a NetworkBehaviour. {foundType.TypeDefinition.Name} is not a NetworkBehaviour", pd); + } + private void ProcessMethod(MethodDefinition md, FoundType foundType) { if (IgnoreMethod(md)) diff --git a/Assets/Mirage/Weaver/Processors/SyncVarProcessor.cs b/Assets/Mirage/Weaver/Processors/SyncVarProcessor.cs index 412db0a6cee..51f38cd8188 100644 --- a/Assets/Mirage/Weaver/Processors/SyncVarProcessor.cs +++ b/Assets/Mirage/Weaver/Processors/SyncVarProcessor.cs @@ -157,7 +157,7 @@ private void InjectAndCopyAttributes(PropertyDefinition property, FieldDefinitio foreach (var attr in property.CustomAttributes) { var attrType = attr.AttributeType; - if (attrType.Is() || attrType.Namespace == "UnityEngine") + if (attrType.Is() || attrType.Namespace == "UnityEngine" || attrType.Namespace == "Mirage.Serialization") { if (!backingField.CustomAttributes.Any(a => a.AttributeType.FullName == attrType.FullName)) CopyAttribute(attr, backingField); diff --git a/Assets/Tests/Weaver/GeneralTests~/RecursionCount.cs b/Assets/Tests/Weaver/GeneralTests~/RecursionCount.cs index 9b204721da9..7e6e2eba01b 100644 --- a/Assets/Tests/Weaver/GeneralTests~/RecursionCount.cs +++ b/Assets/Tests/Weaver/GeneralTests~/RecursionCount.cs @@ -17,6 +17,6 @@ public class Potato1 } [SyncVar] - Potato0 recursionTime; + Potato0 recursionTime { get; set; } } } diff --git a/Assets/Tests/Weaver/NetworkBehaviourTests~/NetworkBehaviourAbstractBaseValid.cs b/Assets/Tests/Weaver/NetworkBehaviourTests~/NetworkBehaviourAbstractBaseValid.cs index 0e44b7f447c..a3969244dde 100644 --- a/Assets/Tests/Weaver/NetworkBehaviourTests~/NetworkBehaviourAbstractBaseValid.cs +++ b/Assets/Tests/Weaver/NetworkBehaviourTests~/NetworkBehaviourAbstractBaseValid.cs @@ -7,12 +7,12 @@ public abstract class EntityBase : NetworkBehaviour { } public class EntityConcrete : EntityBase { [SyncVar] - public int abstractDerivedSync; + public int abstractDerivedSync { get; set; } } public class NetworkBehaviourAbstractBaseValid : EntityConcrete { [SyncVar] - public int concreteDerivedSync; + public int concreteDerivedSync { get; set; } } } diff --git a/Assets/Tests/Weaver/NetworkBehaviourTests~/NetworkBehaviourValid.cs b/Assets/Tests/Weaver/NetworkBehaviourTests~/NetworkBehaviourValid.cs index c99023f14a3..0a787497285 100644 --- a/Assets/Tests/Weaver/NetworkBehaviourTests~/NetworkBehaviourValid.cs +++ b/Assets/Tests/Weaver/NetworkBehaviourTests~/NetworkBehaviourValid.cs @@ -5,6 +5,6 @@ namespace NetworkBehaviourTests.NetworkBehaviourValid class MirageTestPlayer : NetworkBehaviour { [SyncVar] - public int durpatron9000 = 12; + public int durpatron9000 { get; set; } = 12; } } diff --git a/Assets/Tests/Weaver/SyncVarHookTests.cs b/Assets/Tests/Weaver/SyncVarHookTests.cs index 863940f9321..99e57a9f0dd 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests.cs @@ -59,28 +59,28 @@ public void FindsHookWithOtherOverloadsInReverseOrder() public void ErrorWhenNoHookFound() { HasError($"Could not find hook for 'health', hook name 'onChangeHealth', hook type Automatic. See SyncHookType for valid signatures", - $"System.Int32 {TypeName()}::health"); + $"System.Int32 {TypeName()}::health()"); } [Test] public void ErrorWhenNoHookWithCorrectParametersFound() { HasError($"Could not find hook for 'health', hook name 'onChangeHealth', hook type Automatic. See SyncHookType for valid signatures", - $"System.Int32 {TypeName()}::health"); + $"System.Int32 {TypeName()}::health()"); } [Test] public void ErrorForWrongTypeOldParameter() { HasError($"Wrong type for Parameter in hook for 'health', hook name 'onChangeHealth'.", - $"System.Int32 {TypeName()}::health"); + $"System.Int32 {TypeName()}::health()"); } [Test] public void ErrorForWrongTypeNewParameter() { HasError($"Wrong type for Parameter in hook for 'health', hook name 'onChangeHealth'.", - $"System.Int32 {TypeName()}::health"); + $"System.Int32 {TypeName()}::health()"); } [Test, BatchSafe(BatchType.Success)] @@ -119,7 +119,7 @@ public void SyncVarHookServer() public void SyncVarHookServerError() { HasError($"'invokeHookOnServer' or 'InvokeHookOnOwner' is set to true but no hook was implemented. Please implement hook or set 'invokeHookOnServer' back to false or remove for default false.", - $"System.Int32 {TypeName()}::health"); + $"System.Int32 {TypeName()}::health()"); } [Test, BatchSafe(BatchType.Success)] @@ -150,14 +150,14 @@ public void AutomaticHookEvent2() public void AutomaticNotFound() { HasError("Could not find hook for 'health', hook name 'onChangeHealth', hook type Automatic. See SyncHookType for valid signatures", - $"System.Int32 {TypeName()}::health"); + $"System.Int32 {TypeName()}::health()"); } [Test] public void AutomaticFound2Methods() { HasError("Mutliple hooks found for 'health', hook name 'onChangeHealth'. Please set HookType or remove one of the overloads", - $"System.Int32 {TypeName()}::health"); + $"System.Int32 {TypeName()}::health()"); } [Test, BatchSafe(BatchType.Success)] @@ -200,14 +200,14 @@ public void ExplicitMethod2FoundWithOverLoad() public void ExplicitMethod1NotFound() { HasError("Could not find hook for 'health', hook name 'onChangeHealth', hook type MethodWith1Arg. See SyncHookType for valid signatures", - $"System.Int32 {TypeName()}::health"); + $"System.Int32 {TypeName()}::health()"); } [Test] public void ExplicitMethod2NotFound() { HasError("Could not find hook for 'health', hook name 'onChangeHealth', hook type MethodWith2Arg. See SyncHookType for valid signatures", - $"System.Int32 {TypeName()}::health"); + $"System.Int32 {TypeName()}::health()"); } [Test] @@ -221,7 +221,7 @@ public void ExplicitEvent1NotFound() public void ExplicitEvent2NotFound() { HasError("Could not find hook for 'health', hook name 'onChangeHealth', hook type EventWith2Arg. See SyncHookType for valid signatures", - $"System.Int32 {TypeName()}::health"); + $"System.Int32 {TypeName()}::health()"); } } } diff --git a/Assets/Tests/Weaver/SyncVarTests.cs b/Assets/Tests/Weaver/SyncVarTests.cs index c397362983d..6bc4917185d 100644 --- a/Assets/Tests/Weaver/SyncVarTests.cs +++ b/Assets/Tests/Weaver/SyncVarTests.cs @@ -32,7 +32,7 @@ public void SyncVarsDerivedNetworkBehaviour() public void SyncVarsStatic() { HasError("invalidVar cannot be static", - "System.Int32 SyncVarTests.SyncVarsStatic.SyncVarsStatic::invalidVar"); + "System.Int32 SyncVarTests.SyncVarsStatic.SyncVarsStatic::invalidVar()"); } [Test, BatchSafe(BatchType.Success)] @@ -51,28 +51,28 @@ public void SyncVarsGenericParam() public void SyncVarsInterface() { HasError("Cannot generate write function for interface IMySyncVar. Use a supported type or provide a custom write function", - "SyncVarTests.SyncVarsInterface.SyncVarsInterface/IMySyncVar SyncVarTests.SyncVarsInterface.SyncVarsInterface::invalidVar"); + "SyncVarTests.SyncVarsInterface.SyncVarsInterface/IMySyncVar SyncVarTests.SyncVarsInterface.SyncVarsInterface::invalidVar()"); } [Test] public void SyncVarsUnityComponent() { HasError("Cannot generate write function for component type TextMesh. Use a supported type or provide a custom write function", - "UnityEngine.TextMesh SyncVarTests.SyncVarsUnityComponent.SyncVarsUnityComponent::invalidVar"); + "UnityEngine.TextMesh SyncVarTests.SyncVarsUnityComponent.SyncVarsUnityComponent::invalidVar()"); } [Test] public void SyncVarsCantBeArray() { HasError("thisShouldntWork has invalid type. Use SyncLists instead of arrays", - "System.Int32[] SyncVarTests.SyncVarsCantBeArray.SyncVarsCantBeArray::thisShouldntWork"); + "System.Int32[] SyncVarTests.SyncVarsCantBeArray.SyncVarsCantBeArray::thisShouldntWork()"); } [Test] public void SyncVarsSyncList() { HasError("syncints has [SyncVar] attribute. ISyncObject should not be marked with SyncVar", - "Mirage.Collections.SyncList`1 SyncVarTests.SyncVarsSyncList.SyncVarsSyncList::syncints"); + "Mirage.Collections.SyncList`1 SyncVarTests.SyncVarsSyncList.SyncVarsSyncList::syncints()"); } [Test] From 300b18e35d2a151342f697a452b5e657b47beb4c Mon Sep 17 00:00:00 2001 From: James Frowen Date: Sun, 31 May 2026 10:56:52 +0100 Subject: [PATCH 13/41] fix: removing () from property name --- .../Logging/WeaverLogger.cs | 19 ++++++++++++++---- Assets/Tests/Weaver/SyncVarHookTests.cs | 20 +++++++++---------- Assets/Tests/Weaver/SyncVarTests.cs | 10 +++++----- 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/Assets/Mirage/Weaver/Mirage.CecilExtensions/Logging/WeaverLogger.cs b/Assets/Mirage/Weaver/Mirage.CecilExtensions/Logging/WeaverLogger.cs index 1788cb4d831..f22c3f5d082 100644 --- a/Assets/Mirage/Weaver/Mirage.CecilExtensions/Logging/WeaverLogger.cs +++ b/Assets/Mirage/Weaver/Mirage.CecilExtensions/Logging/WeaverLogger.cs @@ -27,14 +27,25 @@ public void Error(string message) AddMessage(message, null, DiagnosticType.Error); } + private string FormatMember(MemberReference mr) + { + if (mr == null) + return string.Empty; + + if (mr is PropertyReference pr) + return $"{pr.PropertyType.FullName} {pr.DeclaringType.FullName}::{pr.Name}"; + + return mr.ToString(); + } + public void Error(string message, MemberReference mr) { - Error($"{message} (at {mr})"); + Error($"{message} (at {FormatMember(mr)})"); } public void Error(string message, MemberReference mr, SequencePoint sequencePoint) { - AddMessage($"{message} (at {mr})", sequencePoint, DiagnosticType.Error); + AddMessage($"{message} (at {FormatMember(mr)})", sequencePoint, DiagnosticType.Error); } public void Error(string message, MethodDefinition md) @@ -50,12 +61,12 @@ public void Warning(string message) public void Warning(string message, MemberReference mr) { - Warning($"{message} (at {mr})"); + Warning($"{message} (at {FormatMember(mr)})"); } public void Warning(string message, MemberReference mr, SequencePoint sequencePoint) { - AddMessage($"{message} (at {mr})", sequencePoint, DiagnosticType.Warning); + AddMessage($"{message} (at {FormatMember(mr)})", sequencePoint, DiagnosticType.Warning); } public void Warning(string message, MethodDefinition md) diff --git a/Assets/Tests/Weaver/SyncVarHookTests.cs b/Assets/Tests/Weaver/SyncVarHookTests.cs index 99e57a9f0dd..9d8cacf00f5 100644 --- a/Assets/Tests/Weaver/SyncVarHookTests.cs +++ b/Assets/Tests/Weaver/SyncVarHookTests.cs @@ -59,28 +59,28 @@ public void FindsHookWithOtherOverloadsInReverseOrder() public void ErrorWhenNoHookFound() { HasError($"Could not find hook for 'health', hook name 'onChangeHealth', hook type Automatic. See SyncHookType for valid signatures", - $"System.Int32 {TypeName()}::health()"); + $"System.Int32 {TypeName()}::health"); } [Test] public void ErrorWhenNoHookWithCorrectParametersFound() { HasError($"Could not find hook for 'health', hook name 'onChangeHealth', hook type Automatic. See SyncHookType for valid signatures", - $"System.Int32 {TypeName()}::health()"); + $"System.Int32 {TypeName()}::health"); } [Test] public void ErrorForWrongTypeOldParameter() { HasError($"Wrong type for Parameter in hook for 'health', hook name 'onChangeHealth'.", - $"System.Int32 {TypeName()}::health()"); + $"System.Int32 {TypeName()}::health"); } [Test] public void ErrorForWrongTypeNewParameter() { HasError($"Wrong type for Parameter in hook for 'health', hook name 'onChangeHealth'.", - $"System.Int32 {TypeName()}::health()"); + $"System.Int32 {TypeName()}::health"); } [Test, BatchSafe(BatchType.Success)] @@ -119,7 +119,7 @@ public void SyncVarHookServer() public void SyncVarHookServerError() { HasError($"'invokeHookOnServer' or 'InvokeHookOnOwner' is set to true but no hook was implemented. Please implement hook or set 'invokeHookOnServer' back to false or remove for default false.", - $"System.Int32 {TypeName()}::health()"); + $"System.Int32 {TypeName()}::health"); } [Test, BatchSafe(BatchType.Success)] @@ -150,14 +150,14 @@ public void AutomaticHookEvent2() public void AutomaticNotFound() { HasError("Could not find hook for 'health', hook name 'onChangeHealth', hook type Automatic. See SyncHookType for valid signatures", - $"System.Int32 {TypeName()}::health()"); + $"System.Int32 {TypeName()}::health"); } [Test] public void AutomaticFound2Methods() { HasError("Mutliple hooks found for 'health', hook name 'onChangeHealth'. Please set HookType or remove one of the overloads", - $"System.Int32 {TypeName()}::health()"); + $"System.Int32 {TypeName()}::health"); } [Test, BatchSafe(BatchType.Success)] @@ -200,14 +200,14 @@ public void ExplicitMethod2FoundWithOverLoad() public void ExplicitMethod1NotFound() { HasError("Could not find hook for 'health', hook name 'onChangeHealth', hook type MethodWith1Arg. See SyncHookType for valid signatures", - $"System.Int32 {TypeName()}::health()"); + $"System.Int32 {TypeName()}::health"); } [Test] public void ExplicitMethod2NotFound() { HasError("Could not find hook for 'health', hook name 'onChangeHealth', hook type MethodWith2Arg. See SyncHookType for valid signatures", - $"System.Int32 {TypeName()}::health()"); + $"System.Int32 {TypeName()}::health"); } [Test] @@ -221,7 +221,7 @@ public void ExplicitEvent1NotFound() public void ExplicitEvent2NotFound() { HasError("Could not find hook for 'health', hook name 'onChangeHealth', hook type EventWith2Arg. See SyncHookType for valid signatures", - $"System.Int32 {TypeName()}::health()"); + $"System.Int32 {TypeName()}::health"); } } } diff --git a/Assets/Tests/Weaver/SyncVarTests.cs b/Assets/Tests/Weaver/SyncVarTests.cs index 6bc4917185d..c397362983d 100644 --- a/Assets/Tests/Weaver/SyncVarTests.cs +++ b/Assets/Tests/Weaver/SyncVarTests.cs @@ -32,7 +32,7 @@ public void SyncVarsDerivedNetworkBehaviour() public void SyncVarsStatic() { HasError("invalidVar cannot be static", - "System.Int32 SyncVarTests.SyncVarsStatic.SyncVarsStatic::invalidVar()"); + "System.Int32 SyncVarTests.SyncVarsStatic.SyncVarsStatic::invalidVar"); } [Test, BatchSafe(BatchType.Success)] @@ -51,28 +51,28 @@ public void SyncVarsGenericParam() public void SyncVarsInterface() { HasError("Cannot generate write function for interface IMySyncVar. Use a supported type or provide a custom write function", - "SyncVarTests.SyncVarsInterface.SyncVarsInterface/IMySyncVar SyncVarTests.SyncVarsInterface.SyncVarsInterface::invalidVar()"); + "SyncVarTests.SyncVarsInterface.SyncVarsInterface/IMySyncVar SyncVarTests.SyncVarsInterface.SyncVarsInterface::invalidVar"); } [Test] public void SyncVarsUnityComponent() { HasError("Cannot generate write function for component type TextMesh. Use a supported type or provide a custom write function", - "UnityEngine.TextMesh SyncVarTests.SyncVarsUnityComponent.SyncVarsUnityComponent::invalidVar()"); + "UnityEngine.TextMesh SyncVarTests.SyncVarsUnityComponent.SyncVarsUnityComponent::invalidVar"); } [Test] public void SyncVarsCantBeArray() { HasError("thisShouldntWork has invalid type. Use SyncLists instead of arrays", - "System.Int32[] SyncVarTests.SyncVarsCantBeArray.SyncVarsCantBeArray::thisShouldntWork()"); + "System.Int32[] SyncVarTests.SyncVarsCantBeArray.SyncVarsCantBeArray::thisShouldntWork"); } [Test] public void SyncVarsSyncList() { HasError("syncints has [SyncVar] attribute. ISyncObject should not be marked with SyncVar", - "Mirage.Collections.SyncList`1 SyncVarTests.SyncVarsSyncList.SyncVarsSyncList::syncints()"); + "Mirage.Collections.SyncList`1 SyncVarTests.SyncVarsSyncList.SyncVarsSyncList::syncints"); } [Test] From ca4dbe0707fb548fe1c8d61e23270632adc40a70 Mon Sep 17 00:00:00 2001 From: James Frowen Date: Sun, 31 May 2026 14:26:37 +0100 Subject: [PATCH 14/41] docs: updating docs for syncvar as properties --- doc/docs/guides/attributes.md | 167 +++++++++---------- doc/docs/guides/sync/code-generation.md | 6 + doc/docs/guides/sync/custom-serialization.md | 2 +- doc/docs/guides/sync/sync-var.md | 10 ++ 4 files changed, 100 insertions(+), 85 deletions(-) diff --git a/doc/docs/guides/attributes.md b/doc/docs/guides/attributes.md index 26f53993de3..85c15b38734 100644 --- a/doc/docs/guides/attributes.md +++ b/doc/docs/guides/attributes.md @@ -1,84 +1,83 @@ ---- -sidebar_position: 4 ---- -# Attributes - -Networking attributes are added to members of [NetworkBehaviour](/docs/reference/Mirage/NetworkBehaviour) scripts to tell Mirage to do different things. - -There are 4 types of attributes that Mirage has: -- **[RPC Attributes](#rpc-attributes)**: Cause a method to send a network message so that the body of the method is invoked on either the server or client. -- **[Block methods invokes](#block-methods-invokes)**: Attributes used to restrict method invocation to specific contexts. -- **[SyncVar](/docs/guides/sync/sync-var)**: Add to Fields to cause their value to be automatically synced to clients. -- **[Bit Packing](/docs/guides/bit-packing)**: These attributes modify how values are written, providing an easy way to compress values before they are sent over the network. They can be applied to Fields and method Parameters. -- **[MaxLength](#max-length-attribute)**: Restricts the deserialization size of strings and collections (arrays, lists) to protect against memory allocation attacks. It can be applied to Fields and method Parameters. - - - -## RPC Attributes - -Full details on RPC can be found on the [Remote Actions](/docs/guides/remote-actions) page. - -Both Rpc attributes support setting the channel to Reliable or Unreliable. - -:::note -When using abstract or virtual methods, the attributes need to be applied to the override methods too. -::: - -- **[ClientRpcAttribute](/docs/reference/Mirage/ClientAttribute)** - The `ClientRpcAttribute` allows the server to use a Remote Procedure Call (RPC) to run a function on specific clients, with options to target the owner, all observers, or a specified player. - See also: [ClientRpc](/docs/guides/remote-actions/client-rpc) - -- **[ServerRpcAttribute](/docs/reference/Mirage/ServerRpcAttribute)** - The `ServerRpcAttribute` is used when you want to call a function on the server from a client. Make sure to validate the input on the server. Note that you cannot call this attribute from the server itself. You can use this attribute as a wrapper around another function, allowing you to call it from the server as well. Additionally, you can return values from functions marked with this attribute. - See also: [ServerRpc](/docs/guides/remote-actions/server-rpc) - -## Block Methods Invokes - -These attributes can be added to methods to block them from being invoked in the wrong place. These attributes can only be used in `NetworkBehaviour` classes and when the object is spawned. If the object is not spawned, all the flags like `IsServer` will be false so will block the methods even if the server is running. - -By default, methods with these attributes will throw a `MethodInvocationException` if invoked improperly. However, you can add `error = false` to return instead of throwing an exception. - -:::note -When a method returns early due to a blocked invocation, the method will return default values for the return value or out parameters. -::: - -These attributes can be used for Unity game loop methods like `Start`, `Update` or `OnCollisionEnter`, as well as other implemented methods that need to be restricted to certain contexts. - -#### Available Attributes: - -- **[ServerAttribute](#server-attribute):** Methods can only be invoked on the server. -- **[ClientAttribute](#client-attribute):** Methods can only be invoked on the client. -- **[HasAuthorityAttribute](#has-authority-attribute):** Methods can only be invoked on the client when the player has authority of the object. See: [Authority](/docs/guides/authority) -- **[LocalPlayerAttribute](#local-player-attribute):** Methods can only be invoked on the client when the object is the local player. See: [Authority](/docs/guides/game-objects/spawn-player) -- **[NetworkMethodAttribute](#network-method-attribute):** Methods can only be invoked based on the flags set in the attribute. For example, `NetworkFlags.Server | NetworkFlags.HasAuthority` allows the method to be called on the server **OR** on the client with authority. - -#### Examples: - -```cs -[Server] -void SpawnCoin() -{ - // This method is only allowed to be invoked on the server. -} -``` - -```cs -[NetworkMethod(NetworkFlags.Server | NetworkFlags.NotActive)] -public void StartGame() -{ - // This method will run on the server or in single-player mode. - // It will only be blocked if the client is active. -} -``` - -## Max Length Attribute - -The `[MaxLength(int)]` attribute restricts the deserialization size of strings and collections (arrays, lists) per-field or per-parameter. - -It is highly recommended to use this attribute when receiving strings or collections from untrusted clients (e.g., in `[ServerRpc]` parameters or fields in `[NetworkMessage]` structs) to protect your server from **memory allocation attacks**. - -For details and usage, see: -- **RPCs:** [Server Rpc](/docs/guides/remote-actions/server-rpc#protecting-against-memory-allocation-attacks-maxlength-attribute) / [Client Rpc](/docs/guides/remote-actions/client-rpc) -- **SyncVars:** [Sync Var](/docs/guides/sync/sync-var#protecting-syncvars-from-allocation-attacks) -- **Network Messages:** [Network Messages](/docs/guides/remote-actions/network-messages#protecting-network-messages-from-allocation-attacks) -- **Error Handling:** [Error Handling](/docs/guides/error-handling#player-error-flags) \ No newline at end of file +--- +sidebar_position: 4 +--- +# Attributes + +Networking attributes are added to members of [NetworkBehaviour](/docs/reference/Mirage/NetworkBehaviour) scripts to tell Mirage to do different things. + +There are 5 types of attributes that Mirage has: +- **[RPC Attributes](#rpc-attributes)**: Cause a method to send a network message so that the body of the method is invoked on either the server or client. +- **[Block methods invokes](#block-methods-invokes)**: Attributes used to restrict method invocation to specific contexts. +- **[SyncVar](/docs/guides/sync/sync-var)**: Add to Properties to cause their value to be automatically synced to clients. +- **[Bit Packing](/docs/guides/bit-packing)**: These attributes modify how values are written, providing an easy way to compress values before they are sent over the network. They can be applied to Network message fields, SyncVar properties and RPC method parameters. +- **[MaxLength](#max-length-attribute)**: Restricts the deserialization size of strings and collections (arrays, lists) to protect against memory allocation attacks. It can be applied to Fields and method Parameters. + + +## RPC Attributes + +Full details on RPC can be found on the [Remote Actions](/docs/guides/remote-actions) page. + +Both Rpc attributes support setting the channel to Reliable or Unreliable. + +:::note +When using abstract or virtual methods, the attributes need to be applied to the override methods too. +::: + +- **[ClientRpcAttribute](/docs/reference/Mirage/ClientAttribute)** + The `ClientRpcAttribute` allows the server to use a Remote Procedure Call (RPC) to run a function on specific clients, with options to target the owner, all observers, or a specified player. + See also: [ClientRpc](/docs/guides/remote-actions/client-rpc) + +- **[ServerRpcAttribute](/docs/reference/Mirage/ServerRpcAttribute)** + The `ServerRpcAttribute` is used when you want to call a function on the server from a client. Make sure to validate the input on the server. Note that you cannot call this attribute from the server itself. You can use this attribute as a wrapper around another function, allowing you to call it from the server as well. Additionally, you can return values from functions marked with this attribute. + See also: [ServerRpc](/docs/guides/remote-actions/server-rpc) + +## Block Methods Invokes + +These attributes can be added to methods to block them from being invoked in the wrong place. These attributes can only be used in `NetworkBehaviour` classes and when the object is spawned. If the object is not spawned, all the flags like `IsServer` will be false so will block the methods even if the server is running. + +By default, methods with these attributes will throw a `MethodInvocationException` if invoked improperly. However, you can add `error = false` to return instead of throwing an exception. + +:::note +When a method returns early due to a blocked invocation, the method will return default values for the return value or out parameters. +::: + +These attributes can be used for Unity game loop methods like `Start`, `Update` or `OnCollisionEnter`, as well as other implemented methods that need to be restricted to certain contexts. + +#### Available Attributes: + +- **[ServerAttribute](#server-attribute):** Methods can only be invoked on the server. +- **[ClientAttribute](#client-attribute):** Methods can only be invoked on the client. +- **[HasAuthorityAttribute](#has-authority-attribute):** Methods can only be invoked on the client when the player has authority of the object. See: [Authority](/docs/guides/authority) +- **[LocalPlayerAttribute](#local-player-attribute):** Methods can only be invoked on the client when the object is the local player. See: [Authority](/docs/guides/game-objects/spawn-player) +- **[NetworkMethodAttribute](#network-method-attribute):** Methods can only be invoked based on the flags set in the attribute. For example, `NetworkFlags.Server | NetworkFlags.HasAuthority` allows the method to be called on the server **OR** on the client with authority. + +#### Examples: + +```cs +[Server] +void SpawnCoin() +{ + // This method is only allowed to be invoked on the server. +} +``` + +```cs +[NetworkMethod(NetworkFlags.Server | NetworkFlags.NotActive)] +public void StartGame() +{ + // This method will run on the server or in single-player mode. + // It will only be blocked if the client is active. +} +``` + +## Max Length Attribute + +The `[MaxLength(int)]` attribute restricts the deserialization size of strings and collections (arrays, lists) per-field or per-parameter. + +It is highly recommended to use this attribute when receiving strings or collections from untrusted clients (e.g., in `[ServerRpc]` parameters or fields in `[NetworkMessage]` structs) to protect your server from **memory allocation attacks**. + +For details and usage, see: +- **RPCs:** [Server Rpc](/docs/guides/remote-actions/server-rpc#protecting-against-memory-allocation-attacks-maxlength-attribute) / [Client Rpc](/docs/guides/remote-actions/client-rpc) +- **SyncVars:** [Sync Var](/docs/guides/sync/sync-var#protecting-syncvars-from-allocation-attacks) +- **Network Messages:** [Network Messages](/docs/guides/remote-actions/network-messages#protecting-network-messages-from-allocation-attacks) +- **Error Handling:** [Error Handling](/docs/guides/error-handling#player-error-flags) diff --git a/doc/docs/guides/sync/code-generation.md b/doc/docs/guides/sync/code-generation.md index dc51d3968b0..99777051a52 100644 --- a/doc/docs/guides/sync/code-generation.md +++ b/doc/docs/guides/sync/code-generation.md @@ -3,6 +3,12 @@ sidebar_position: 7 --- # Code Generation +To synchronize variables, Mirage uses property-based `[SyncVar]` definitions. During the build process, the Weaver intercepts each auto-property, completely clearing its compiled getter and setter method bodies. It then injects custom IL directly into these accessors: +- **Getter**: Directly returns the backing field (e.g. `ldfld`). +- **Setter**: Injects an equality check (`SyncVarEqual`), a dirty flag update (`SetDirtyBit`), hook execution checks, and updates the backing field. + +The compiler also generates `SerializeSyncVars` and `DeserializeSyncVars` methods to serialize/deserialize these values. + So for this script: ```cs diff --git a/doc/docs/guides/sync/custom-serialization.md b/doc/docs/guides/sync/custom-serialization.md index 3f504f757fd..6e97c583163 100644 --- a/doc/docs/guides/sync/custom-serialization.md +++ b/doc/docs/guides/sync/custom-serialization.md @@ -24,4 +24,4 @@ The `OnSerialize` function should return true to indicate that an update should The `OnSerialize` function is only called for `initialState` or when the [NetworkBehaviour](/docs/reference/Mirage/NetworkBehaviour) is dirty. A [NetworkBehaviour](/docs/reference/Mirage/NetworkBehaviour) will only be dirty if a `SyncVar` or `SyncObject` (e.g. `SyncList`) has changed since the last OnSerialize call. After data has been sent the [NetworkBehaviour](/docs/reference/Mirage/NetworkBehaviour) will not be dirty again until the next `syncInterval` (set in the inspector). A [NetworkBehaviour](/docs/reference/Mirage/NetworkBehaviour) can also be marked as dirty by manually calling `SetDirtyBit` (this does not bypass the `syncInterval` limit). -Although this works, it is usually better to let Mirage generate these methods and provide [custom serializers](/docs/guides/serialization/data-types) for your specific field. +Although this works, it is usually better to let Mirage generate these methods and provide [custom serializers](/docs/guides/serialization/data-types) for your specific property. diff --git a/doc/docs/guides/sync/sync-var.md b/doc/docs/guides/sync/sync-var.md index 0ed093ea883..1bd38a37a9b 100644 --- a/doc/docs/guides/sync/sync-var.md +++ b/doc/docs/guides/sync/sync-var.md @@ -17,6 +17,16 @@ The server automatically sends SyncVar updates when the value of a SyncVar chang SyncVars are not sent right away or in the order they are set. They will be sent as a group in the next sync update. ::: +## Why Properties Instead of Fields? +Mirage uses properties `{ get; set; }` for `[SyncVar]` variables instead of fields to naturally intercept variable reads and writes without rewriting user code at all the access locations. + +When a property is accessed, C# executes standard getter and setter methods under the hood. During compilation, Mirage's Weaver intercepts these methods, clears their compiler-generated bodies, and injects serialization, dirty tracking, and hook dispatching logic. This enables clean, transparent synchronization that behaves identically across all calling contexts. + +### Auto-Property Requirement +`[SyncVar]` properties **must** be simple auto-properties. They cannot have custom `get` or `set` accessors. + +Because the Weaver completely clears the compiled method body of the getter and setter to inject the synchronization logic, any custom logic written inside user-defined accessors would be lost. The Weaver verifies that only simple compiler-generated auto-properties (which only read/write directly to their backing field) are used, and will throw a `SyncVarException` at build time if any custom logic is detected. + ## Example Let's have a simple `Player` class with the following code: From 93172d1db67b2d0aebf236c930aaf893fb609430 Mon Sep 17 00:00:00 2001 From: James Frowen Date: Sun, 31 May 2026 14:28:01 +0100 Subject: [PATCH 15/41] feat: adding Warning when class is used as syncvar, and adding WeaverSyncVarSafe to mark classes and ignore fields --- Assets/Mirage/Runtime/CustomAttributes.cs | 7 ++++ .../Weaver/Processors/SyncVarProcessor.cs | 25 ++++++++++++++ Assets/Tests/Weaver/SyncVarTests.cs | 8 +++++ .../SyncVarTests~/SyncVarClassWarning.cs | 34 +++++++++++++++++++ 4 files changed, 74 insertions(+) create mode 100644 Assets/Tests/Weaver/SyncVarTests~/SyncVarClassWarning.cs diff --git a/Assets/Mirage/Runtime/CustomAttributes.cs b/Assets/Mirage/Runtime/CustomAttributes.cs index 60f52fd63b0..334f86d7e69 100644 --- a/Assets/Mirage/Runtime/CustomAttributes.cs +++ b/Assets/Mirage/Runtime/CustomAttributes.cs @@ -36,6 +36,13 @@ public class SyncVarAttribute : PropertyAttribute public SyncHookType hookType = SyncHookType.Automatic; } + /// + /// Prevents Weaver warnings for class-type SyncVars. + /// Apply to a class or SyncVar property to acknowledge that custom serialization handles safety and allocation concerns. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property)] + public class WeaverSyncVarSafeAttribute : Attribute { } + public enum SyncHookType { /// diff --git a/Assets/Mirage/Weaver/Processors/SyncVarProcessor.cs b/Assets/Mirage/Weaver/Processors/SyncVarProcessor.cs index 51f38cd8188..998af4737ea 100644 --- a/Assets/Mirage/Weaver/Processors/SyncVarProcessor.cs +++ b/Assets/Mirage/Weaver/Processors/SyncVarProcessor.cs @@ -6,6 +6,7 @@ using Mirage.Weaver.SyncVars; using Mono.Cecil; using Mono.Cecil.Cil; +using UnityEngine; namespace Mirage.Weaver { @@ -43,6 +44,8 @@ public void ProcessSyncVars(TypeDefinition td, IWeaverLogger logger) { if (IsValidSyncVar(pd, out var fd)) { + ValidateSyncVarType(pd, logger); + InjectAndCopyAttributes(pd, fd); var syncVar = behaviour.AddSyncVar(pd, fd); @@ -103,6 +106,28 @@ private bool IsValidSyncVar(PropertyDefinition property, out FieldDefinition bac return true; } + private void ValidateSyncVarType(PropertyDefinition property, IWeaverLogger logger) + { + var type = property.PropertyType; + if (type.Is() || type.Is() || type.Is()) + return; + + var resolved = type.Resolve(); + if (resolved == null) + return; + + if (resolved.IsDerivedFrom()) + return; + + if (resolved.IsClass && !resolved.IsValueType && resolved.FullName != "System.String") + { + if (property.HasCustomAttribute() || resolved.HasCustomAttribute()) + return; + + logger.Warning($"{property.Name} is a class. SyncVars that are classes can allocate and are difficult to track changes for. Consider using a struct instead, or mark the class or property with [WeaverSyncVarSafe] if it is custom serialized.", property); + } + } + private bool IsSimpleGetter(PropertyDefinition property, FieldDefinition backingField) { if (property.GetMethod?.Body == null) diff --git a/Assets/Tests/Weaver/SyncVarTests.cs b/Assets/Tests/Weaver/SyncVarTests.cs index c397362983d..e78e6a08b9c 100644 --- a/Assets/Tests/Weaver/SyncVarTests.cs +++ b/Assets/Tests/Weaver/SyncVarTests.cs @@ -81,5 +81,13 @@ public void SyncVarsMoreThan63() HasError("SyncVarsMoreThan63 has too many [SyncVar]. Consider refactoring your class into multiple components", "SyncVarTests.SyncVarsMoreThan63.SyncVarsMoreThan63"); } + + [Test] + public void SyncVarClassWarning() + { + NoErrors(); + HasWarning("warnedVar is a class. SyncVars that are classes can allocate and are difficult to track changes for. Consider using a struct instead, or mark the class or property with [WeaverSyncVarSafe] if it is custom serialized.", + "SyncVarTests.SyncVarClassWarning.SomeCustomClass SyncVarTests.SyncVarClassWarning.SyncVarClassWarning::warnedVar"); + } } } diff --git a/Assets/Tests/Weaver/SyncVarTests~/SyncVarClassWarning.cs b/Assets/Tests/Weaver/SyncVarTests~/SyncVarClassWarning.cs new file mode 100644 index 00000000000..680cf3a148c --- /dev/null +++ b/Assets/Tests/Weaver/SyncVarTests~/SyncVarClassWarning.cs @@ -0,0 +1,34 @@ +using Mirage; + +namespace SyncVarTests.SyncVarClassWarning +{ + class SomeCustomClass + { + public int Value; + } + + [WeaverSyncVarSafe] + class SafeClass + { + public int Value; + } + + class SyncVarClassWarning : NetworkBehaviour + { + [SyncVar] + SomeCustomClass warnedVar { get; set; } + + [SyncVar] + [WeaverSyncVarSafe] + SomeCustomClass safePropVar { get; set; } + + [SyncVar] + SafeClass safeClassVar { get; set; } + + [SyncVar] + NetworkBehaviour networkBehaviourVar { get; set; } + + [SyncVar] + string stringVar { get; set; } + } +} From 828793272ead5e3742ab771aa7563fd2de1a1ffa Mon Sep 17 00:00:00 2001 From: James Frowen Date: Sun, 31 May 2026 14:55:23 +0100 Subject: [PATCH 16/41] fix: changing to WeaverSafeClassAttribute and applying warning to networkmessage and rpc as well --- Assets/Mirage/Runtime/CustomAttributes.cs | 20 ++++++-- .../Extensions/TypeExtensions.cs | 48 ++++++++++++++++++ .../Mirage/Weaver/Processors/RpcProcessor.cs | 7 +++ .../Weaver/Processors/SyncVarProcessor.cs | 18 +------ Assets/Mirage/Weaver/Serialization/Writers.cs | 5 ++ Assets/Mirage/Weaver/SerializeFunctionBase.cs | 3 +- Assets/Tests/Weaver/SyncVarTests.cs | 16 +++++- .../RpcAndMessageClassWarnings.cs | 49 +++++++++++++++++++ 8 files changed, 143 insertions(+), 23 deletions(-) create mode 100644 Assets/Tests/Weaver/SyncVarTests~/RpcAndMessageClassWarnings.cs diff --git a/Assets/Mirage/Runtime/CustomAttributes.cs b/Assets/Mirage/Runtime/CustomAttributes.cs index 334f86d7e69..b6e801bb5a2 100644 --- a/Assets/Mirage/Runtime/CustomAttributes.cs +++ b/Assets/Mirage/Runtime/CustomAttributes.cs @@ -37,11 +37,23 @@ public class SyncVarAttribute : PropertyAttribute } /// - /// Prevents Weaver warnings for class-type SyncVars. - /// Apply to a class or SyncVar property to acknowledge that custom serialization handles safety and allocation concerns. + /// Prevents Weaver warnings when using class-type SyncVars, NetworkMessage fields, or RPC parameters/return values. + /// A class-type is generally UNSAFE because: + /// + /// It will allocate a new object upon deserialization. + /// Mirage cannot track internal changes to the class. + /// It will fail SyncVar equality checks if setting the same instance (preventing dirty bit setting). + /// + /// + /// Mark a type or member with this attribute to declare it SAFE because: + /// + /// It utilizes custom serialization/deserialization that manages safety/allocations. + /// It is serialized by ID/reference (like NetworkBehaviour/NetworkIdentity/GameObject). + /// + /// /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property)] - public class WeaverSyncVarSafeAttribute : Attribute { } + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Method)] + public class WeaverSafeClassAttribute : Attribute { } public enum SyncHookType { diff --git a/Assets/Mirage/Weaver/Mirage.CecilExtensions/Extensions/TypeExtensions.cs b/Assets/Mirage/Weaver/Mirage.CecilExtensions/Extensions/TypeExtensions.cs index f3338102d60..b9c0bba1681 100644 --- a/Assets/Mirage/Weaver/Mirage.CecilExtensions/Extensions/TypeExtensions.cs +++ b/Assets/Mirage/Weaver/Mirage.CecilExtensions/Extensions/TypeExtensions.cs @@ -345,5 +345,53 @@ public static FieldDefinition GetField(this TypeDefinition type, string fieldNam return null; } + + public static bool IsDerivedFrom(this TypeDefinition td, string baseClassFullName) + { + if (td == null) + return false; + + if (!td.IsClass) + return false; + + var parent = td.BaseType; + if (parent == null) + return false; + + if (parent.FullName == baseClassFullName) + return true; + + if (parent.CanBeResolved()) + return IsDerivedFrom(parent.Resolve(), baseClassFullName); + + return false; + } + + public static bool IsUnsafeClass(this TypeReference type, ICustomAttributeProvider attributeProvider = null) + { + var fullName = type.FullName; + if (fullName == "Mirage.NetworkIdentity" || fullName == "UnityEngine.GameObject" || fullName == "Mirage.NetworkBehaviour") + return false; + + var resolved = type.TryResolve(); + if (resolved == null) + return false; + + if (resolved.IsDerivedFrom("Mirage.NetworkBehaviour")) + return false; + + if (resolved.IsClass && !resolved.IsValueType && resolved.FullName != "System.String") + { + if (resolved.CustomAttributes.Any(attr => attr.AttributeType.FullName == "Mirage.WeaverSafeClassAttribute")) + return false; + + if (attributeProvider != null && attributeProvider.CustomAttributes.Any(attr => attr.AttributeType.FullName == "Mirage.WeaverSafeClassAttribute")) + return false; + + return true; + } + + return false; + } } } diff --git a/Assets/Mirage/Weaver/Processors/RpcProcessor.cs b/Assets/Mirage/Weaver/Processors/RpcProcessor.cs index 093ba31bbc0..879365b6b99 100644 --- a/Assets/Mirage/Weaver/Processors/RpcProcessor.cs +++ b/Assets/Mirage/Weaver/Processors/RpcProcessor.cs @@ -308,6 +308,10 @@ protected ReturnType ValidateReturnType(MethodDefinition md, RemoteCallType call { var genericReturnType = (GenericInstanceType)returnType; var genericArg = genericReturnType.GenericArguments[0]; + + if (genericArg.IsUnsafeClass(md)) + logger.Warning($"Return type UniTask<{genericArg.Name}> is a class. RPC return values that are classes can allocate. Consider using a struct instead, or mark the class or method with [WeaverSafeClass] if it is custom serialized.", md); + // ensure serialize functions exist _ = writers.GetFunction_Throws(genericArg); _ = readers.GetFunction_Throws(genericArg); @@ -349,6 +353,9 @@ private void ValidateParameter(MethodReference method, ParameterDefinition param { throw new RpcException($"{method.Name} cannot have optional parameters", method); } + + if (param.ParameterType.IsUnsafeClass(param)) + logger.Warning($"{param.Name} is a class. RPC parameters that are classes can allocate. Consider using a struct instead, or mark the class or parameter with [WeaverSafeClass] if it is custom serialized.", method); } diff --git a/Assets/Mirage/Weaver/Processors/SyncVarProcessor.cs b/Assets/Mirage/Weaver/Processors/SyncVarProcessor.cs index 998af4737ea..df0783204bc 100644 --- a/Assets/Mirage/Weaver/Processors/SyncVarProcessor.cs +++ b/Assets/Mirage/Weaver/Processors/SyncVarProcessor.cs @@ -108,23 +108,9 @@ private bool IsValidSyncVar(PropertyDefinition property, out FieldDefinition bac private void ValidateSyncVarType(PropertyDefinition property, IWeaverLogger logger) { - var type = property.PropertyType; - if (type.Is() || type.Is() || type.Is()) - return; - - var resolved = type.Resolve(); - if (resolved == null) - return; - - if (resolved.IsDerivedFrom()) - return; - - if (resolved.IsClass && !resolved.IsValueType && resolved.FullName != "System.String") + if (property.PropertyType.IsUnsafeClass(property)) { - if (property.HasCustomAttribute() || resolved.HasCustomAttribute()) - return; - - logger.Warning($"{property.Name} is a class. SyncVars that are classes can allocate and are difficult to track changes for. Consider using a struct instead, or mark the class or property with [WeaverSyncVarSafe] if it is custom serialized.", property); + logger.Warning($"{property.Name} is a class. SyncVars that are classes can allocate and are difficult to track changes for. Consider using a struct instead, or mark the class or property with [WeaverSafeClass] if it is custom serialized.", property); } } diff --git a/Assets/Mirage/Weaver/Serialization/Writers.cs b/Assets/Mirage/Weaver/Serialization/Writers.cs index 3d207808ee3..f130b60d22a 100644 --- a/Assets/Mirage/Weaver/Serialization/Writers.cs +++ b/Assets/Mirage/Weaver/Serialization/Writers.cs @@ -156,6 +156,11 @@ private void WriteAllFields(TypeReference type, WriteMethod writerFunc) var fieldType = fieldDef.GetFieldTypeIncludingGeneric(type); var fieldRef = module.ImportField(fieldDef, type); + if (fieldType.IsUnsafeClass(fieldDef)) + { + logger.Warning($"{fieldDef.Name} is a class. Serializing classes in messages can allocate and are difficult to track changes for. Consider using a struct instead, or mark the class or field with [WeaverSafeClass] if it is custom serialized.", fieldDef); + } + var valueSerialize = ValueSerializerFinder.GetSerializer(module, fieldDef, fieldType, this, null); valueSerialize.AppendWriteField(module, writerFunc.worker, writerFunc.writerParameter, writerFunc.typeParameter, fieldRef); } diff --git a/Assets/Mirage/Weaver/SerializeFunctionBase.cs b/Assets/Mirage/Weaver/SerializeFunctionBase.cs index b469799bf22..9edf162757e 100644 --- a/Assets/Mirage/Weaver/SerializeFunctionBase.cs +++ b/Assets/Mirage/Weaver/SerializeFunctionBase.cs @@ -16,7 +16,6 @@ private static void Log(string msg) { Console.Write($"[Weaver.SerializeFunction] {msg}\n"); } - /// Concrete type serialization function lookup. protected readonly Dictionary functionLookup = new Dictionary(new TypeReferenceComparer()); /// Open generic serialization helper method lookup. @@ -25,7 +24,7 @@ private static void Log(string msg) protected readonly Dictionary functionWithLengthLookup = new Dictionary(new TypeReferenceComparer()); /// Open generic length-limited serialization helper method lookup. protected readonly Dictionary genericWithLengthLookup = new Dictionary(new TypeReferenceComparer()); - private readonly IWeaverLogger logger; + protected readonly IWeaverLogger logger; protected readonly ModuleDefinition module; /// Count used to see if we generated any new ones, meaning the dll is changed and needs writing diff --git a/Assets/Tests/Weaver/SyncVarTests.cs b/Assets/Tests/Weaver/SyncVarTests.cs index e78e6a08b9c..840d5720188 100644 --- a/Assets/Tests/Weaver/SyncVarTests.cs +++ b/Assets/Tests/Weaver/SyncVarTests.cs @@ -86,8 +86,22 @@ public void SyncVarsMoreThan63() public void SyncVarClassWarning() { NoErrors(); - HasWarning("warnedVar is a class. SyncVars that are classes can allocate and are difficult to track changes for. Consider using a struct instead, or mark the class or property with [WeaverSyncVarSafe] if it is custom serialized.", + HasWarning("warnedVar is a class. SyncVars that are classes can allocate and are difficult to track changes for. Consider using a struct instead, or mark the class or property with [WeaverSafeClass] if it is custom serialized.", "SyncVarTests.SyncVarClassWarning.SomeCustomClass SyncVarTests.SyncVarClassWarning.SyncVarClassWarning::warnedVar"); } + + [Test] + public void RpcAndMessageClassWarnings() + { + NoErrors(); + HasWarning("UnsafeField is a class. Serializing classes in messages can allocate and are difficult to track changes for. Consider using a struct instead, or mark the class or field with [WeaverSafeClass] if it is custom serialized.", + "SyncVarTests.RpcAndMessageClassWarnings.UnsafeClass SyncVarTests.RpcAndMessageClassWarnings.TestMessage::UnsafeField"); + + HasWarning("unsafeParam is a class. RPC parameters that are classes can allocate. Consider using a struct instead, or mark the class or parameter with [WeaverSafeClass] if it is custom serialized.", + "System.Void SyncVarTests.RpcAndMessageClassWarnings.RpcAndMessageClassWarnings::SendUnsafeRpc(SyncVarTests.RpcAndMessageClassWarnings.UnsafeClass)"); + + HasWarning("Return type UniTask is a class. RPC return values that are classes can allocate. Consider using a struct instead, or mark the class or method with [WeaverSafeClass] if it is custom serialized.", + "Cysharp.Threading.Tasks.UniTask`1 SyncVarTests.RpcAndMessageClassWarnings.RpcAndMessageClassWarnings::UnsafeReturnRpc()"); + } } } diff --git a/Assets/Tests/Weaver/SyncVarTests~/RpcAndMessageClassWarnings.cs b/Assets/Tests/Weaver/SyncVarTests~/RpcAndMessageClassWarnings.cs new file mode 100644 index 00000000000..206b2b170ee --- /dev/null +++ b/Assets/Tests/Weaver/SyncVarTests~/RpcAndMessageClassWarnings.cs @@ -0,0 +1,49 @@ +using Mirage; +using Cysharp.Threading.Tasks; + +namespace SyncVarTests.RpcAndMessageClassWarnings +{ + class UnsafeClass + { + public int Value; + } + + [WeaverSafeClass] + class SafeClass + { + public int Value; + } + + [NetworkMessage] + struct TestMessage + { + public UnsafeClass UnsafeField; + + [WeaverSafeClass] + public UnsafeClass SafeField; + + public SafeClass SafeClassField; + } + + class RpcAndMessageClassWarnings : NetworkBehaviour + { + [ServerRpc] + public void SendUnsafeRpc(UnsafeClass unsafeParam) { } + + [ServerRpc] + public void SendSafeParamRpc([WeaverSafeClass] UnsafeClass safeParam) { } + + [ServerRpc] + public void SendSafeClassRpc(SafeClass safeClassParam) { } + + [ServerRpc] + public UniTask UnsafeReturnRpc() => UniTask.FromResult(null); + + [ServerRpc] + [WeaverSafeClass] + public UniTask SafeReturnRpc() => UniTask.FromResult(null); + + [ServerRpc] + public UniTask SafeClassReturnRpc() => UniTask.FromResult(null); + } +} From dfe921a67f2d4c81808d2016c0f1e855b272f86b Mon Sep 17 00:00:00 2001 From: James Frowen Date: Sun, 31 May 2026 16:11:58 +0100 Subject: [PATCH 17/41] feat: adding c# Analyzers --- Assets/Mirage/Analyzers.meta | 8 + Assets/Mirage/Analyzers/Mirage.Analyzers.dll | Bin 0 -> 18944 bytes .../Analyzers/Mirage.Analyzers.dll.meta | 72 +++++ .../NetworkBehaviourAttributeAnalyzer.cs | 115 ++++++++ Mirage.Analyzers/RpcSignatureAnalyzer.cs | 89 ++++++ Mirage.Analyzers/WeaverSafeClassAnalyzer.cs | 255 ++++++++++++++++++ doc/docs/analyzers/MIRAGE1001.md | 70 +++++ doc/docs/analyzers/MIRAGE1002.md | 37 +++ doc/docs/analyzers/MIRAGE1101.md | 36 +++ doc/docs/analyzers/MIRAGE1201.md | 59 ++++ doc/docs/analyzers/MIRAGE1301.md | 72 +++++ doc/docs/analyzers/_category_.json | 4 + 12 files changed, 817 insertions(+) create mode 100644 Assets/Mirage/Analyzers.meta create mode 100644 Assets/Mirage/Analyzers/Mirage.Analyzers.dll create mode 100644 Assets/Mirage/Analyzers/Mirage.Analyzers.dll.meta create mode 100644 Mirage.Analyzers/NetworkBehaviourAttributeAnalyzer.cs create mode 100644 Mirage.Analyzers/RpcSignatureAnalyzer.cs create mode 100644 Mirage.Analyzers/WeaverSafeClassAnalyzer.cs create mode 100644 doc/docs/analyzers/MIRAGE1001.md create mode 100644 doc/docs/analyzers/MIRAGE1002.md create mode 100644 doc/docs/analyzers/MIRAGE1101.md create mode 100644 doc/docs/analyzers/MIRAGE1201.md create mode 100644 doc/docs/analyzers/MIRAGE1301.md create mode 100644 doc/docs/analyzers/_category_.json diff --git a/Assets/Mirage/Analyzers.meta b/Assets/Mirage/Analyzers.meta new file mode 100644 index 00000000000..bf043bb2b46 --- /dev/null +++ b/Assets/Mirage/Analyzers.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a7d8e2f3b9c04d6e9f1a2b3c4d5e6f7a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Analyzers/Mirage.Analyzers.dll b/Assets/Mirage/Analyzers/Mirage.Analyzers.dll new file mode 100644 index 0000000000000000000000000000000000000000..f0914ae43ffa909acbf4d3c5ac354e4fdcebc90d GIT binary patch literal 18944 zcmeHP3v?W3b-pvRJNuBd(ynC5jxBrrmc7ziFWZtGCzkcF1(xkdvV)0Td$l`~Hs0Nl zXIG8|iKCDQC5<7bKpP+r!s8H{v$fB*aVXLi;@ufLr%B68vL(o002M9tTBQC_&1#c*KRrvmh_ z@0sPFRCYhJd~`CI4NqC-gcVDN3#h~ zyA_Rw8sBrH!|gd*5w1{LiFN>@OXwv>QHSxF#fPXyv{flL16Y1>eFi1qd}(y_W=7?I zh1%{i2-lNGi1u=0mgqCAi1Ohl#vJ<6iW474@Sz<75u~*6k?5 z`0!e7xzQ3=nCNKK%35*sglw~D@Iw3WDY~{}yP}qnGJ(jl(mnVvtzw4gZkJrpDqJsO z{d_U1N^ZI*N^~hyuL$TXFIK2c0U|%GAF~^3!|Bx^x4K???{y&RT7Q`{pv~z+X4m@Y zDJ;>|1l-8_Z<1DTV)y){nL$z2ozp!qifjnIMW@#><*!sTek}lI9pkL8ZPn?cn2&!! zs-~s|fckZ_9#ut1X=*@S>k7J>-Cb`3N_`~(xR8PF$W!n zTNkRUZ(7BzpxC%zaN%hTV8d;GLYjM5H|Vm_1Z(iuhN>wTYFdLPONRBZX11()>7|#} z6VfAy=t6~1 zs=J}q6>0FApzE(BZ{0bq)*W=WJXsGg1zqc5un`QA;Oiq+FJD$0cEZ}p#TU4^OQESn z4c13cS-()RNf0iQZNbc0HrJ)6*=JVQTWO7^uvqI(Q7^Qf7WE?Q1yNsOb+~wZv2{$; zORT#^-DrKC>n06EXW4%8^s3#GUYn>FT33jAk#$_umsod-da?BdQ7^H6D(XfH2JFyd z^MzNzs%)?Nhxxp!%JHfO=~ea1e0Syj$xB~_P9R<0pK`ES#jmro>oDgE4=nXT;OOQC zR=kdWly&~5FtpIft(vakBz~ZBGwG!)gy>(;4d5`2M4JK2=WVi zRp|yu2ZITcS|@hFRbLsgRI?poEIFsZ{#^(@G1ctgwdlYRk&m%<%Pp|J$J$}{5ggk| zx5d+gy7e<>tR;Zn1`kb{oxqGJ4I=h2Z8rjFguLZ`gzXcTbsUqdH*@i8E^I&A=0*EO z6h(f-2D<*hDP%C$DOjWH6l}}YgiVQvwd(ZeHqH6W7m;5k1;>1s*w(8dV;}*P4lb)M z8bw|x8?7oDMYbp#E%FN&#UT?Mtl$@6;TIwfG$DHG@CtUxlJpq15fCYXC&G5q#ekQzdGq^P`*3B-iYjv|5 zbxVcb1h-|6!VZGbCK8G+_t6GBfEP#Xf^S9!cr{xJtJ!pc)d-uC>){#Jw@cUK5=R+q zd|^FEFIbPc71+}pV=6o_TF9zKmU)_xcy-vcBzP$ktn?QYRNLyJPe4DuVtRI*;+Ykm z?)P9A+Tq|bjXckUmw3%>=r-4R&C5{NB2+b9jz;}CRl02wZo8dFI11GU-O|1DZUdxR z_)2pR1>Lsm;9Ouev0%cjyg_gCO3-kq*EjVrHJ_>neO9I*KP2NlR_Xm{`FRcO(|*0# z(@@cM1~Ap!fq|9pyumTQbCEwC!4R zl~#aC`{seQsyOvDhh$xXVoU9?5O&Wz5E+7q2nfsURcd6H=Hjrd3+vb_0*59JB~>A< zG}~xm&ybg0vLW(-znbhUV8<6W4g?;D2gtg+ZUxL{1F_V|buP0H%9lqZJ5*w=jk>kI z!rt(fD&0Ejbl_-gAbi|ZBilttlQP!2LvAVLBHP{Ekk1ixVX>eU)#Q=sO6vO~km(OE zS7~$_awDc+=uBs4Uu>W_kyl3chE#dhb>XlklW(2f5L+oveyn2l&ai_d9Z{YGg?A#8 zdLlK*q^dlGe;B8Cq)}M#@AZ4?Q4o`OSV5m5_M= zbwy+gZUb;6^;eN^I*jNg%xWRju?xWFYBZ24a46%jqfw4$pE~ z1GN}9#KkYT;L`x8-%^kEvkfKaocyslJ4Rl!L$U8h`QG+KXS6li(c005gvKYI6w2F? zKvte2`VDFhoGV9iRx&e@WsKk8MsXh^#>)L8v>OL~m;kNZxqlE&Ooz~a4vw^PN6H*y zWL#LDa^;=(uJeHoy{L2$P6rB_$ljb+c+Utam%{i!p?vnD75Fd?k7-387Kh>dVK~=d zhb!skHbNJTx_<4^=`m4ewfjBQv>4NLdQhu&>+~VlCv=@EMfsG*?H`FUCm?r-_I8aa zeBG78sOIDHDlMh^DdKv}h2kq&EQ7tW^Wlh|*6vAHy@EJmo&=_0z+|Gt7^=fPA1y`kZV75i6*4k)R4WZoNtw*^YWikA~Y!z@0 z%q|z@FNyId1%6JHSEB4tvec;U3-g+8S$M0^-NAfsxu%en@2x@aP5B;=y-zE#uc9c;K*M^^9AppTW@ll)+FqIYKD?VeqlPX8f#lBQ0- zls!SyL#tLvDu1nP)x6}wjU3awP1&va=n=bj&^@gAX>bjXNe&e>DteMb1>G)sl0yZ3 zSoQ>mO1gBdq&Wf(Rdm1Id(O923(&Pqa?I!1s#VjM?H58p!5lTO;Ls>&@qU;uBk0=L4d4(ts ziZX^$rx}zBaKC!1XE7xOq)l0?t)TnpZQc%)&mn450=|Q;P(J9}1<1YnFg--KxZgo% zsmFT<)zLS6_o2Mj^8hWOgT6=Uv-ETC;~2eJ{Q|9`zx93O+i+8rpL(iyVHeYe7E{D7h; z52z`{r+i3x2Ia#j&(h;qp-;fiQiJ{u3X1kq^l|k*dW!0N|D$wLuUl200=37fQ%L(f z{ToJ~r=v%GV0`2+Cf% ziY8F{Q4R`faRG@7NF4XbDOXa!Zxrww1^h<9|I+n-#L!Qn4AEy$9-}8+PYB2p0`i1_ zd``^$p@9ETz<(&&DPriK=qlL=rm6OV8X!#N4Ys$A3U2Ra8su6X&dO+nk zufroeA|LE&f`n7UGy-p1u}9m6v%PIc+dymQ=8n#;jXS!!x(B*9b$9n~ zZ0qW3Z*OhwZtoiC-rOFJ@K9L`>2(>bFrmW$DZ zkvlS&m2%OrF=bl0zGQ47V`d>J^<-kHnKu|iMm8IpF!ox*Q*kQp6i^2fG%}NkABb5c zu%79hxz94E3@bNNG9dZ#8k6y4E;Z9<#8WZLNH{D9l13^aq+=z$Hko1i=`k}UnsW9| zBV$;}_>hsCG!rzKg>-g1FSi(B?43r=9^)Q1$nIr~Ts9ZWBx082)@wrH$qWvsdtzxL zI~9u?LW-U#mQ-v7)|rc)95%)c%gA6@_V?gxGsw_?(pk|^%sOTyMq=ZPk&Jy zIF^I2u|%8kSef>o+u9S5!R(G$HW_DO^B8~>I)m8*vD9>-Urx)O;1d)rxjp(u<%ky zPd00$$5Jz+u&pA*(U=7-4B)#@;~LXCR-oYE4?vm&hLwfK%^e)fjGIhD9nTdeI$Gep!JZceoEc0x zmYFFG*rq4ox#U?6kU^W5vw1S5?RQJW$?sA5e2iSqp?&Di|5hv|#R)jn0tcV`Aa-o^M)?j85fiIUG zu*@{|#t?*H|C}X8&0|J}b{e_eri@hx=@Ur=ADgF4SNVN1#_f0{xa_r(6G;U8K4TmK zkjYVhhTRHFiBDSycDeqOMtoX~1^Z9fS*G zSy)xvGPCA*4xu?AWy&VA(P9+d%8}_Q&WlE(c)%v-*t!$RMhxpjGHzt;I0f5BUPpIi z5)|yk<{dE0TUFF^Jea+U$B^DQC>9GRhvpX?;Z%kmW8SGe%(*Qqea^NJA;S=YC8C~_ zi*^|)SR(7E)a%5=yu4yN3dactC11s zoSp=uUTJeQWS%hg@Vgk2fG_}T6R(D$al3{pWhO3A69=uL5GMQP)RF$I38{o!kv|kjz&dwcwv69qC9DO@#gTh;DO1^BzWZ@XBPKf8}OF~HOB)oLXs%9l{ae=ni#_zw*C!N>?s?>M#n*8 z17ynx`DW-1m~9Ezyk1h~Yo+@$*MO;|m}3Ad3ANc*_>oZ>Z|ch1&QKIgZzdH5~A=$3XFT!>G`;R+>vnaq zROi*q>SEc>i`DXqG`3B~bSK!!Y{8z%`GmcRV@MqRDfkF`SsF0T2ka-btaLW}eGWcn zVJ3%f<#G<)O_;-Bn1g32#tHbPj0zm9!n7arl6a2JQS2C=9xBzP5W2hbF?U%-}Bqe$Lj=T`LkW-&+IUwYA;qGwZpNOBVl3PGEcIWGt@Fi zw@y@jCE8JX=I2?QG?UO3r@jnw21kC@^%g*6np9>#T^zU>H4QXAILKsv4JL39`tt;kb#vXbfX}B3PUtCY z6m7UL<`ndA!U=e1a&QY;>=?vcSEc^n*6>FO@1`tsIF#-cb7wiq6JDwOgIJS1s<2f- z-WKVc-0p{oa?0j_6UEU~hMB!c-d6(>2R>b1s`V=Y=YuV;egv&?wAdEqp>iI*Z{*a# zLCAoW^8;!V*eUdwIv)`DSi-!E!yX|pUwjs62n@?FgWnv%-wwzSM_Y!Yfx>>4g^pGk zI^v}%Yku#E!PDaT)rE!rSmS*3=A$?tjpq6m|a-hv1O1kdEN59YeI105Ss-( zz7Gh4yNSPJfUJY5a`2UAD^Jsm%X!w}$2-afZvo0i&KgfbswqgwPT)ABlrApj%GJ!( znp_`q<9(I)08X-S_;IS>T`o4%LJGNxF6^?0g?uup$UWt@P@Q@u>M(?TImd}Lg? zKu2}udG_Ut&#})rJe4|H!Kj>#a?D1WR&jJ|m8_gk32aa2x1`cV9u~OHG2B^5dH5>VufwI_VCBNg09n2S z3FUy}EUh%mO8&*ldv#89YGTZBrnR)h|^M!=sfWVQj{22xxsf!PE(&3$hlcPuu z7rfIKj)|GT|66;iNiT5l_v4xNL3nD%F{QiffF<< zfWvipnw>9UyjtmAuK53Fs^m1E7CFhnqprDk2E~EtGPoAZ>1m(U` zdU7lEoVlX&v%BIOkw1h~eEu^nKWCqS8@)P#&<=Uz4G+KjrXMQ<58c=B$lI-Z*ODu& zC|)g0iW`k!kh=lV?AF7o5@-xm)F{e=;1$YJT1qaz5*pMQarREng3#>yl;A-((3ULp zk*b7d9|+Dq6hPk|Y7EYPupM}Zd44FSh75H!GI_y&d zxPr5v$3SDCl2rvEmNG)n;|X1-s-fd@QD`v#8N8s&Q|de(xdyp9sgMefSFi*d6`}`c zp8;PrP+g;FjK({_e;$t(n!Q0)Ydm^r_U0lM%-}ko#}%4=3$tF}@uLz-hx%vG2&kbM zM*%ZZ0W(mY*YELSrQ#BNzp8s&+^Ynw3-0?>T)L zP(HOO1Ik0^JivA;14_@(^hUR;<6l72VdA>5B~1^mVux_EO*Fb4@dt&8>~|CV;2>Tl zgHi){iA(`N4P7V784rUGhh}`F0f}3Fa@nnbm~$8v@MmxKHc3k#^eXnx#h3D1%%f`k zl~!zOkD2K|88@cHm+zC7c}-RU9^S*mwNs%*(LMd6c&$OaU({^B53%h;d$bi40(JQp zQS2{8`MrZ6PYFA7!a&9A%r$eqi6y^6Zt*S*esu$mZFuP(e?%P$MMWpljaR{98{_d! zo4dyoow1Frn_J_t@vivz#*WU;&52EkO-5^5rxC*|?=0^cyz+XCUy>VDt9nu?^O~XQ zR4$2k&;an+ZyezLETHYiPx&r}<`%Ddqr_E#SG#+~8_9TQUA*R9{C3{c=Jo$e%FsR) zm?l20NAT(4dGcZswfOK$#dl-u?xHcq;NJ@rU&AAPBcE=3;={|oxN7I${KePb_JiZM z-On`owj9FSB)J)UnakffXAeo8+EwAzl0)X$)rW*;4;8%}hJBcW8J$Xu(a5fz_Klm! z=J+j#BUW|IjSB)#=bmpoeeL@``pkbra_90MMJw#|N^9rW{t}9}NUXk8Y6#!jQ#uOC|d|JMzA)b+D-(qGw&_>%SIvV-4gl)hQZ z;68j5`+_3O`q6b}{LqJd&!FZ~ zx<}w}Hj#gwIBG)&cnBQCTz)>s&)b;C3|1r`YcuuIhuJ+C=i9BqH_(jp8bQP1*(YwY z;*dBAiq0eJmrD!(3j*YEXdM78eySiJ6)}xAoI6|dALhLP_^h$wC!mEDTulBbZVLHH zC|^y8)ri^PIU!cbTA9LHc_kBgriX|7A{Y$|o_lRL-&?az9GSVtH!~O4T`_OD5AxAT z0w-*K4q8~n#c8qpZ^C_sg75eE!IMLyT!RJuMW`Ep?bHQ%H-cXrzYE(8$T;S93Ro-p zt$_2x`7YGs!0W(RC*TQS^Tz;28zB4ynJ@gm0Fa>vd&W1~d_yOnGM`@#-p}9BbyWVJ TF-v}D1b;_z{fW%)MFal_KlwHe literal 0 HcmV?d00001 diff --git a/Assets/Mirage/Analyzers/Mirage.Analyzers.dll.meta b/Assets/Mirage/Analyzers/Mirage.Analyzers.dll.meta new file mode 100644 index 00000000000..36b7f968cb3 --- /dev/null +++ b/Assets/Mirage/Analyzers/Mirage.Analyzers.dll.meta @@ -0,0 +1,72 @@ +fileFormatVersion: 2 +guid: 4a3b7d1e0c2f4e8b8a5d8f6c6f7e8a9b +labels: +- RoslynAnalyzer +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : + second: + enabled: 0 + settings: {} + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: RoslynAnalyzer + assetBundleName: + assetBundleVariant: diff --git a/Mirage.Analyzers/NetworkBehaviourAttributeAnalyzer.cs b/Mirage.Analyzers/NetworkBehaviourAttributeAnalyzer.cs new file mode 100644 index 00000000000..18500e6fa17 --- /dev/null +++ b/Mirage.Analyzers/NetworkBehaviourAttributeAnalyzer.cs @@ -0,0 +1,115 @@ +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Mirage.Analyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class NetworkBehaviourAttributeAnalyzer : DiagnosticAnalyzer + { + public const string DiagnosticId = "MIRAGE1101"; + + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + DiagnosticId, + "Network attributes can only be used on NetworkBehaviour classes", + "Attribute '{0}' cannot be used on '{1}' because its declaring class does not inherit from NetworkBehaviour", + "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Network attributes like SyncVar, Server, Client, HasAuthority, LocalPlayer, ServerRpc, ClientRpc, and NetworkMethod are only valid inside NetworkBehaviour classes.", + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1101"); + + private static readonly ImmutableHashSet NetworkAttributes = ImmutableHashSet.Create( + "Mirage.SyncVarAttribute", + "Mirage.ServerAttribute", + "Mirage.ClientAttribute", + "Mirage.HasAuthorityAttribute", + "Mirage.LocalPlayerAttribute", + "Mirage.ServerRpcAttribute", + "Mirage.ClientRpcAttribute", + "Mirage.NetworkMethodAttribute" + ); + + private static readonly ImmutableHashSet ShortNetworkAttributes = ImmutableHashSet.Create( + "SyncVarAttribute", + "ServerAttribute", + "ClientAttribute", + "HasAuthorityAttribute", + "LocalPlayerAttribute", + "ServerRpcAttribute", + "ClientRpcAttribute", + "NetworkMethodAttribute" + ); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Field, SymbolKind.Property, SymbolKind.Method); + } + + private static void AnalyzeSymbol(SymbolAnalysisContext context) + { + var symbol = context.Symbol; + + var attributes = symbol.GetAttributes(); + if (attributes.IsEmpty) + return; + + var hasNetworkAttribute = false; + foreach (var attr in attributes) + { + if (attr.AttributeClass != null && ShortNetworkAttributes.Contains(attr.AttributeClass.Name)) + { + var fullName = attr.AttributeClass.ToDisplayString(); + if (NetworkAttributes.Contains(fullName)) + { + hasNetworkAttribute = true; + break; + } + } + } + + if (!hasNetworkAttribute) + return; + + var containingType = symbol.ContainingType; + + // Stop analysis if the declaring type inherits from NetworkBehaviour, as network attributes are valid in this context. + if (containingType != null && IsOrInheritsFrom(containingType, "Mirage.NetworkBehaviour")) + return; + + foreach (var attr in attributes) + { + if (attr.AttributeClass != null && ShortNetworkAttributes.Contains(attr.AttributeClass.Name)) + { + var fullName = attr.AttributeClass.ToDisplayString(); + if (NetworkAttributes.Contains(fullName)) + { + var location = attr.ApplicationSyntaxReference?.GetSyntax()?.GetLocation() ?? symbol.Locations[0]; + var diagnostic = Diagnostic.Create(Rule, location, attr.AttributeClass.Name, symbol.Name); + context.ReportDiagnostic(diagnostic); + } + } + } + } + + private static bool IsOrInheritsFrom(ITypeSymbol typeSymbol, string fullyQualifiedName) + { + var lastDot = fullyQualifiedName.LastIndexOf('.'); + var shortName = lastDot >= 0 ? fullyQualifiedName.Substring(lastDot + 1) : fullyQualifiedName; + + var current = typeSymbol; + while (current != null) + { + if (current.Name == shortName && current.ToDisplayString() == fullyQualifiedName) + return true; + current = current.BaseType; + } + return false; + } + } +} diff --git a/Mirage.Analyzers/RpcSignatureAnalyzer.cs b/Mirage.Analyzers/RpcSignatureAnalyzer.cs new file mode 100644 index 00000000000..5143188d5f0 --- /dev/null +++ b/Mirage.Analyzers/RpcSignatureAnalyzer.cs @@ -0,0 +1,89 @@ +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Mirage.Analyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class RpcSignatureAnalyzer : DiagnosticAnalyzer + { + public const string DiagnosticId = "MIRAGE1201"; + + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + DiagnosticId, + "RPC method must be non-generic and return void or UniTask", + "RPC method '{0}' is invalid: {1}", + "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Methods marked with ServerRpc or ClientRpc cannot be generic and must return void or UniTask.", + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1201"); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterSymbolAction(AnalyzeMethod, SymbolKind.Method); + } + + private static void AnalyzeMethod(SymbolAnalysisContext context) + { + var methodSymbol = (IMethodSymbol)context.Symbol; + + // Stop analysis if the method is not a ServerRpc or ClientRpc, as RPC rules only apply to them. + if (!IsRpcMethod(methodSymbol)) + return; + + if (methodSymbol.IsGenericMethod) + { + var diagnostic = Diagnostic.Create(Rule, methodSymbol.Locations[0], methodSymbol.Name, "cannot have generic parameters"); + context.ReportDiagnostic(diagnostic); + } + + if (!IsVoidOrUniTask(methodSymbol.ReturnType)) + { + var diagnostic = Diagnostic.Create(Rule, methodSymbol.Locations[0], methodSymbol.Name, $"cannot return '{methodSymbol.ReturnType.ToDisplayString()}' (must return void or UniTask)"); + context.ReportDiagnostic(diagnostic); + } + } + + private static bool IsRpcMethod(IMethodSymbol methodSymbol) + { + foreach (var attr in methodSymbol.GetAttributes()) + { + if (attr.AttributeClass != null && (attr.AttributeClass.Name == "ServerRpcAttribute" || attr.AttributeClass.Name == "ClientRpcAttribute")) + { + var fullName = attr.AttributeClass.ToDisplayString(); + if (fullName == "Mirage.ServerRpcAttribute" || fullName == "Mirage.ClientRpcAttribute") + return true; + } + } + return false; + } + + private static bool IsVoidOrUniTask(ITypeSymbol typeSymbol) + { + if (typeSymbol == null) + return false; + + if (typeSymbol.SpecialType == SpecialType.System_Void) + return true; + + var originalDefinition = typeSymbol.OriginalDefinition; + if (originalDefinition == null) + return false; + + if (originalDefinition.Name == "UniTask") + { + var originalString = originalDefinition.ToDisplayString(); + if (originalString == "Cysharp.Threading.Tasks.UniTask" || originalString.StartsWith("Cysharp.Threading.Tasks.UniTask<")) + return true; + } + + return false; + } + } +} diff --git a/Mirage.Analyzers/WeaverSafeClassAnalyzer.cs b/Mirage.Analyzers/WeaverSafeClassAnalyzer.cs new file mode 100644 index 00000000000..b0105f9c5b4 --- /dev/null +++ b/Mirage.Analyzers/WeaverSafeClassAnalyzer.cs @@ -0,0 +1,255 @@ +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Mirage.Analyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class WeaverSafeClassAnalyzer : DiagnosticAnalyzer + { + public const string SyncVarDiagnosticId = "MIRAGE1001"; + public const string AutoPropertyDiagnosticId = "MIRAGE1002"; + public const string MessageOrRpcDiagnosticId = "MIRAGE1301"; + + private static readonly DiagnosticDescriptor SyncVarRule = new DiagnosticDescriptor( + SyncVarDiagnosticId, + "SyncVar cannot be a class type unless marked safe", + "SyncVar '{0}' is a class type '{1}'. Class-based SyncVars allocate memory, do not support polymorphism (only declared fields serialize), and cannot track internal changes automatically (meaning modifications won't trigger sync hooks). Consider using a struct, implementing custom serialization and marking the class with [WeaverSafeClass], or decorating this SyncVar with [WeaverSafeClass] to ignore.", + "Usage", + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: "Class types used as SyncVars should be value types or marked with [WeaverSafeClass] to avoid allocations and hook tracking issues.", + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1001"); + + private static readonly DiagnosticDescriptor AutoPropertyRule = new DiagnosticDescriptor( + AutoPropertyDiagnosticId, + "SyncVar property must be an auto-property", + "SyncVar property '{0}' must be a non-static auto-property with both get and set accessors", + "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Properties marked with [SyncVar] must be automatic properties with both getter and setter, and cannot be static.", + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1002"); + + private static readonly DiagnosticDescriptor MessageOrRpcRule = new DiagnosticDescriptor( + MessageOrRpcDiagnosticId, + "Class type used in NetworkMessage or RPC without WeaverSafeClass attribute", + "{0} '{1}' is a class type '{2}'. Class-based types allocate memory upon deserialization and do not support polymorphism (only declared fields serialize). Consider using a struct, implementing custom serialization and marking the class with [WeaverSafeClass], or decorating this member/parameter with [WeaverSafeClass] to ignore.", + "Usage", + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: "Class types used as NetworkMessage fields or RPC parameters/returns should be value types or marked with [WeaverSafeClass] to avoid allocations and polymorphism bugs.", + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1301"); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(SyncVarRule, AutoPropertyRule, MessageOrRpcRule); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterSymbolAction(AnalyzeField, SymbolKind.Field); + context.RegisterSymbolAction(AnalyzeProperty, SymbolKind.Property); + context.RegisterSymbolAction(AnalyzeParameter, SymbolKind.Parameter); + context.RegisterSymbolAction(AnalyzeMethod, SymbolKind.Method); + } + + private static void AnalyzeField(SymbolAnalysisContext context) + { + var fieldSymbol = (IFieldSymbol)context.Symbol; + + if (HasAttribute(fieldSymbol, "Mirage.SyncVarAttribute")) + { + AnalyzeSyncVar(context, fieldSymbol, fieldSymbol.Type); + return; + } + + if (fieldSymbol.ContainingType != null && HasAttribute(fieldSymbol.ContainingType, "Mirage.NetworkMessageAttribute")) + { + AnalyzeMessageOrRpc(context, fieldSymbol, fieldSymbol.Type, "NetworkMessage field"); + } + } + + private static void AnalyzeProperty(SymbolAnalysisContext context) + { + var propertySymbol = (IPropertySymbol)context.Symbol; + + if (HasAttribute(propertySymbol, "Mirage.SyncVarAttribute")) + { + if (propertySymbol.GetMethod == null || propertySymbol.SetMethod == null || propertySymbol.IsStatic || !IsAutoProperty(propertySymbol)) + { + var diagnostic = Diagnostic.Create(AutoPropertyRule, propertySymbol.Locations[0], propertySymbol.Name); + context.ReportDiagnostic(diagnostic); + return; + } + + AnalyzeSyncVar(context, propertySymbol, propertySymbol.Type); + return; + } + + if (propertySymbol.ContainingType != null && HasAttribute(propertySymbol.ContainingType, "Mirage.NetworkMessageAttribute")) + { + AnalyzeMessageOrRpc(context, propertySymbol, propertySymbol.Type, "NetworkMessage property"); + } + } + + private static void AnalyzeParameter(SymbolAnalysisContext context) + { + var parameterSymbol = (IParameterSymbol)context.Symbol; + var containingMethod = parameterSymbol.ContainingSymbol as IMethodSymbol; + + if (containingMethod == null) + return; + + if (IsRpcMethod(containingMethod)) + { + AnalyzeMessageOrRpc(context, parameterSymbol, parameterSymbol.Type, "RPC parameter"); + } + } + + private static void AnalyzeMethod(SymbolAnalysisContext context) + { + var methodSymbol = (IMethodSymbol)context.Symbol; + + if (IsRpcMethod(methodSymbol)) + { + var returnType = methodSymbol.ReturnType as INamedTypeSymbol; + if (returnType != null && returnType.IsGenericType && returnType.TypeArguments.Length > 0) + { + var originalDefinition = returnType.OriginalDefinition; + if (originalDefinition != null && originalDefinition.Name == "UniTask") + { + var originalString = originalDefinition.ToDisplayString(); + if (originalString == "Cysharp.Threading.Tasks.UniTask") + { + var typeArgument = returnType.TypeArguments[0]; + AnalyzeMessageOrRpc(context, methodSymbol, typeArgument, "RPC return type"); + } + } + } + } + } + + private static bool IsBasicSafeType(ITypeSymbol typeSymbol) + { + if (typeSymbol == null) + return true; + + if (typeSymbol.IsValueType || typeSymbol.TypeKind == TypeKind.Struct || typeSymbol.TypeKind == TypeKind.Enum) + return true; + + if (typeSymbol.TypeKind != TypeKind.Class) + return true; + + if (typeSymbol.SpecialType == SpecialType.System_String) + return true; + + if (IsOrInheritsFrom(typeSymbol, "Mirage.NetworkIdentity")) + return true; + if (IsOrInheritsFrom(typeSymbol, "UnityEngine.GameObject")) + return true; + if (IsOrInheritsFrom(typeSymbol, "Mirage.NetworkBehaviour")) + return true; + + return false; + } + + private static bool IsExplicitlyMarkedSafe(ISymbol symbol, ITypeSymbol typeSymbol) + { + if (HasAttribute(symbol, "Mirage.WeaverSafeClassAttribute")) + return true; + + if (typeSymbol != null && HasAttribute(typeSymbol, "Mirage.WeaverSafeClassAttribute")) + return true; + + if (symbol.ContainingType != null && HasAttribute(symbol.ContainingType, "Mirage.WeaverSafeClassAttribute")) + return true; + + return false; + } + + private static void AnalyzeSyncVar(SymbolAnalysisContext context, ISymbol symbol, ITypeSymbol typeSymbol) + { + if (IsBasicSafeType(typeSymbol)) + return; + + if (IsExplicitlyMarkedSafe(symbol, typeSymbol)) + return; + + var diagnostic = Diagnostic.Create(SyncVarRule, symbol.Locations[0], symbol.Name, typeSymbol.Name); + context.ReportDiagnostic(diagnostic); + } + + private static void AnalyzeMessageOrRpc(SymbolAnalysisContext context, ISymbol symbol, ITypeSymbol typeSymbol, string contextName) + { + if (IsBasicSafeType(typeSymbol)) + return; + + if (IsExplicitlyMarkedSafe(symbol, typeSymbol)) + return; + + var ns = typeSymbol.ContainingNamespace?.ToDisplayString(); + if (ns != null && (ns == "System.Collections.Generic" || ns == "System.Collections")) + return; + + var diagnostic = Diagnostic.Create(MessageOrRpcRule, symbol.Locations[0], contextName, symbol.Name, typeSymbol.Name); + context.ReportDiagnostic(diagnostic); + } + + private static bool IsRpcMethod(IMethodSymbol methodSymbol) + { + foreach (var attr in methodSymbol.GetAttributes()) + { + if (attr.AttributeClass != null && (attr.AttributeClass.Name == "ServerRpcAttribute" || attr.AttributeClass.Name == "ClientRpcAttribute")) + { + var fullName = attr.AttributeClass.ToDisplayString(); + if (fullName == "Mirage.ServerRpcAttribute" || fullName == "Mirage.ClientRpcAttribute") + return true; + } + } + return false; + } + + private static bool HasAttribute(ISymbol symbol, string fullyQualifiedName) + { + var lastDot = fullyQualifiedName.LastIndexOf('.'); + var shortName = lastDot >= 0 ? fullyQualifiedName.Substring(lastDot + 1) : fullyQualifiedName; + + foreach (var attr in symbol.GetAttributes()) + { + if (attr.AttributeClass != null && attr.AttributeClass.Name == shortName) + { + if (attr.AttributeClass.ToDisplayString() == fullyQualifiedName) + return true; + } + } + return false; + } + + private static bool IsOrInheritsFrom(ITypeSymbol typeSymbol, string fullyQualifiedName) + { + var lastDot = fullyQualifiedName.LastIndexOf('.'); + var shortName = lastDot >= 0 ? fullyQualifiedName.Substring(lastDot + 1) : fullyQualifiedName; + + var current = typeSymbol; + while (current != null) + { + if (current.Name == shortName && current.ToDisplayString() == fullyQualifiedName) + return true; + current = current.BaseType; + } + return false; + } + + private static bool IsAutoProperty(IPropertySymbol propertySymbol) + { + var backingFieldName = $"<{propertySymbol.Name}>k__BackingField"; + foreach (var member in propertySymbol.ContainingType.GetMembers()) + { + if (member is IFieldSymbol field && field.IsImplicitlyDeclared && field.Name == backingFieldName) + return true; + } + return false; + } + } +} diff --git a/doc/docs/analyzers/MIRAGE1001.md b/doc/docs/analyzers/MIRAGE1001.md new file mode 100644 index 00000000000..f9dbebdc649 --- /dev/null +++ b/doc/docs/analyzers/MIRAGE1001.md @@ -0,0 +1,70 @@ +# MIRAGE1001: SyncVar Class Warning + +## The Problem +A property marked with `[SyncVar]` is declared using a class type instead of a value type/struct. + +Class-based types are generally risky for network synchronization because: +1. **Allocations:** Mirage must allocate a new object instance upon deserialization, which causes garbage collection (GC) spikes and performance overhead. +2. **Polymorphism Limitations:** Mirage's standard serialization only serializes fields of the declared property type, not the concrete derived subclass type. +3. **Change Tracking Gotchas:** If you modify an internal field of the class instance on the server, its object reference stays the same. Because the reference hasn't changed, Mirage cannot automatically detect the modification to set the dirty flag and fire sync hooks. +4. **Collection Syncing:** Modifying contents of lists/dictionaries inside a SyncVar will not trigger a synchronization because the container reference does not change. + +--- + +## Example of Triggering Code +```csharp +public class PlayerData +{ + public int health; + public string name; +} + +public class Player : NetworkBehaviour +{ + // Warns: SyncVar 'data' is a class type 'PlayerData'. + [SyncVar] + public PlayerData data { get; set; } +} +``` + +--- + +## How to Resolve + +### Option 1: Use a struct (Recommended) +Structs (value types) avoid memory allocation, support standard change tracking, and guarantee safe value-copy semantics. +```csharp +public struct PlayerData +{ + public int health; + public string name; +} + +public class Player : NetworkBehaviour +{ + [SyncVar] + public PlayerData data { get; set; } +} +``` + +### Option 2: Implement Custom Serialization and mark the class as safe +If you want to use the class type and manage performance/reference safety yourself, write custom `Write` and `Read` extension methods for the class, and decorate the class with `[WeaverSafeClass]` to suppress the warning globally. +```csharp +[WeaverSafeClass] +public class PlayerData +{ + public int health; + public string name; +} +``` + +### Option 3: Suppress the warning on the property +If you want to disable the warning only on a specific property, decorate the property with `[WeaverSafeClass]`. +```csharp +public class Player : NetworkBehaviour +{ + [SyncVar] + [WeaverSafeClass] + public PlayerData data { get; set; } +} +``` diff --git a/doc/docs/analyzers/MIRAGE1002.md b/doc/docs/analyzers/MIRAGE1002.md new file mode 100644 index 00000000000..a8c68636505 --- /dev/null +++ b/doc/docs/analyzers/MIRAGE1002.md @@ -0,0 +1,37 @@ +# MIRAGE1002: SyncVar Auto-Property Error + +## The Problem +A property marked with `[SyncVar]` is not configured as an automatic property, is static, or lacks a getter/setter. + +Mirage's IL Weaver post-processes compiled assemblies by intercepting property writes to set dirty flags and run synchronization hooks. For this injection to succeed, the property **must** be a non-static automatic property (e.g. `public int Health { get; set; }`) so that the weaver can replace the compiler-generated backing field reads and writes. + +--- + +## Example of Triggering Code +```csharp +public class Player : NetworkBehaviour +{ + private int _health; + + // Errors: SyncVar property 'health' must be a non-static auto-property... + [SyncVar] + public int health + { + get => _health; + set => _health = value; + } +} +``` + +--- + +## How to Resolve + +Change the property to a standard auto-property with both `get` and `set` accessors. +```csharp +public class Player : NetworkBehaviour +{ + [SyncVar] + public int health { get; set; } +} +``` diff --git a/doc/docs/analyzers/MIRAGE1101.md b/doc/docs/analyzers/MIRAGE1101.md new file mode 100644 index 00000000000..1a7c6da3134 --- /dev/null +++ b/doc/docs/analyzers/MIRAGE1101.md @@ -0,0 +1,36 @@ +# MIRAGE1101: Misplaced Network Attribute Error + +## The Problem +A Mirage-specific attribute (such as `[SyncVar]`, `[Server]`, `[Client]`, `[ServerRpc]`, `[ClientRpc]`, `[HasAuthority]`, `[LocalPlayer]`, or `[NetworkMethod]`) is declared on a field, property, or method inside a class that does not inherit from `NetworkBehaviour`. + +These attributes control network synchronization or inject runtime active checks (guards) that require the state and context of a `NetworkBehaviour` instance. Using them in standard `MonoBehaviour` or plain C# classes is invalid and will cause compile or weaver failures. + +--- + +## Example of Triggering Code +```csharp +using UnityEngine; +using Mirage; + +public class GameManager : MonoBehaviour +{ + // Errors: Attribute 'SyncVarAttribute' cannot be used on 'score'... + [SyncVar] + public int score { get; set; } +} +``` + +--- + +## How to Resolve + +Ensure that the declaring class inherits from `NetworkBehaviour` instead of `MonoBehaviour` or other base classes. +```csharp +using Mirage; + +public class GameManager : NetworkBehaviour +{ + [SyncVar] + public int score { get; set; } +} +``` diff --git a/doc/docs/analyzers/MIRAGE1201.md b/doc/docs/analyzers/MIRAGE1201.md new file mode 100644 index 00000000000..a2ee97efec8 --- /dev/null +++ b/doc/docs/analyzers/MIRAGE1201.md @@ -0,0 +1,59 @@ +# MIRAGE1201: RPC Signature Error + +## The Problem +A method decorated with `[ServerRpc]` or `[ClientRpc]` violates remote procedure call rules: +1. **Generic Methods:** The method cannot have generic parameters (e.g. `void MyRpc()`). +2. **Invalid Return Type:** The method must return `void`, `UniTask`, or `UniTask`. Returning standard tasks, custom classes, or primitive values is invalid. + +Remote procedure calls must serialize their arguments and return values across the network. Non-generic signatures and async return wrappers (`UniTask`) are required for the Mirage Weaver to generate remote execution logic correctly. + +--- + +## Example of Triggering Code +```csharp +using Mirage; +using Cysharp.Threading.Tasks; + +public class Player : NetworkBehaviour +{ + // Errors: RPC method 'CmdTakeDamage' is invalid: cannot have generic parameters. + [ServerRpc] + public void CmdTakeDamage(T damage) + { + } + + // Errors: RPC method 'CmdGetStats' is invalid: cannot return 'PlayerStats'... + [ServerRpc] + public PlayerStats CmdGetStats() + { + return new PlayerStats(); + } +} +``` + +--- + +## How to Resolve + +1. Make the method non-generic. +2. Ensure the return type is `void` or a valid async task wrapper like `UniTask` (or `UniTask` for asynchronous RPCs). + +```csharp +using Mirage; +using Cysharp.Threading.Tasks; + +public class Player : NetworkBehaviour +{ + [ServerRpc] + public void CmdTakeDamage(int damage) + { + } + + [ServerRpc] + public async UniTask CmdGetStats() + { + await UniTask.Yield(); + return new PlayerStats(); + } +} +``` diff --git a/doc/docs/analyzers/MIRAGE1301.md b/doc/docs/analyzers/MIRAGE1301.md new file mode 100644 index 00000000000..59cfadae647 --- /dev/null +++ b/doc/docs/analyzers/MIRAGE1301.md @@ -0,0 +1,72 @@ +# MIRAGE1301: Message or RPC Class Warning + +## The Problem +A field or property in a class/struct marked with `[NetworkMessage]`, or a parameter/return type argument in a method marked with `[ServerRpc]` or `[ClientRpc]`, uses a class type instead of a value type/struct. + +Class-based types are generally risky for network messaging because: +1. **Allocations:** Mirage must allocate a new object instance upon deserialization, which causes garbage collection (GC) spikes and performance overhead. +2. **Polymorphism Limitations:** Mirage's standard serialization only serializes fields of the declared member type, not the concrete derived subclass type. + +*Note: Standard collections (such as `List` or `Dictionary` in `System.Collections.Generic`) are automatically ignored by this rule as they are natively supported by Mirage's Weaver and serialization system.* + +--- + +## Example of Triggering Code +```csharp +using Mirage; + +public class TargetInfo +{ + public int x; + public int y; +} + +[NetworkMessage] +public struct FireMessage +{ + // Warns: NetworkMessage field 'info' is a class type 'TargetInfo'. + public TargetInfo info; +} +``` + +--- + +## How to Resolve + +### Option 1: Use a struct (Recommended) +Structs (value types) avoid memory allocations and guarantee safe copy-by-value semantics. +```csharp +public struct TargetInfo +{ + public int x; + public int y; +} + +[NetworkMessage] +public struct FireMessage +{ + public TargetInfo info; +} +``` + +### Option 2: Implement Custom Serialization and mark the class as safe +If you want to use the class type and manage performance/reference safety yourself, write custom `Write` and `Read` extension methods for the class, and decorate the class with `[WeaverSafeClass]` to suppress the warning globally. +```csharp +[WeaverSafeClass] +public class TargetInfo +{ + public int x; + public int y; +} +``` + +### Option 3: Suppress the warning on the member or parameter +If you want to disable the warning only on a specific field, property, or parameter, decorate it with `[WeaverSafeClass]`. +```csharp +[NetworkMessage] +public struct FireMessage +{ + [WeaverSafeClass] + public TargetInfo info; +} +``` diff --git a/doc/docs/analyzers/_category_.json b/doc/docs/analyzers/_category_.json new file mode 100644 index 00000000000..59d0bf963b9 --- /dev/null +++ b/doc/docs/analyzers/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Analyzers", + "position": 6 +} From 26f2c374cc77a1cb8ca5fef457b0cfc6f4d2c8a8 Mon Sep 17 00:00:00 2001 From: James Frowen Date: Sun, 31 May 2026 16:20:48 +0100 Subject: [PATCH 18/41] docs: analyzer rules --- doc/docs/analyzers/MIRAGE1003.md | 59 ++++++++++++++++ doc/docs/analyzers/MIRAGE1004.md | 48 +++++++++++++ doc/docs/analyzers/MIRAGE1202.md | 46 +++++++++++++ doc/docs/analyzers/MIRAGE1203.md | 43 ++++++++++++ doc/docs/analyzers/MIRAGE1204.md | 60 +++++++++++++++++ doc/docs/analyzers/MIRAGE1205.md | 46 +++++++++++++ doc/docs/analyzers/MIRAGE1206.md | 42 ++++++++++++ doc/docs/analyzers/MIRAGE1302.md | 38 +++++++++++ doc/docs/analyzers/MIRAGE1303.md | 62 +++++++++++++++++ doc/docs/analyzers/MIRAGE1401.md | 50 ++++++++++++++ doc/docs/analyzers/MIRAGE1402.md | 64 ++++++++++++++++++ doc/docs/analyzers/MIRAGE1501.md | 38 +++++++++++ doc/docs/analyzers/MIRAGE1502.md | 39 +++++++++++ doc/docs/analyzers/MIRAGE1503.md | 46 +++++++++++++ doc/docs/analyzers/index.md | 112 +++++++++++++++++++++++++++++++ 15 files changed, 793 insertions(+) create mode 100644 doc/docs/analyzers/MIRAGE1003.md create mode 100644 doc/docs/analyzers/MIRAGE1004.md create mode 100644 doc/docs/analyzers/MIRAGE1202.md create mode 100644 doc/docs/analyzers/MIRAGE1203.md create mode 100644 doc/docs/analyzers/MIRAGE1204.md create mode 100644 doc/docs/analyzers/MIRAGE1205.md create mode 100644 doc/docs/analyzers/MIRAGE1206.md create mode 100644 doc/docs/analyzers/MIRAGE1302.md create mode 100644 doc/docs/analyzers/MIRAGE1303.md create mode 100644 doc/docs/analyzers/MIRAGE1401.md create mode 100644 doc/docs/analyzers/MIRAGE1402.md create mode 100644 doc/docs/analyzers/MIRAGE1501.md create mode 100644 doc/docs/analyzers/MIRAGE1502.md create mode 100644 doc/docs/analyzers/MIRAGE1503.md create mode 100644 doc/docs/analyzers/index.md diff --git a/doc/docs/analyzers/MIRAGE1003.md b/doc/docs/analyzers/MIRAGE1003.md new file mode 100644 index 00000000000..ee877e581d8 --- /dev/null +++ b/doc/docs/analyzers/MIRAGE1003.md @@ -0,0 +1,59 @@ +# MIRAGE1003: Direct Mutation of SyncCollection Elements + +## The Problem +A member or element within a `SyncList` or `SyncDictionary` is mutated directly without assigning it back to the collection or calling a change notification method. + +Because C# structs are value types, modifying an element's member directly after retrieving it from a collection (e.g. `mySyncList[i].health = 10;`) only modifies a local copy of that struct. Even for reference types (classes), mutating the object directly does not call the indexer setter on the collection. In both cases, the collection cannot detect that a nested property has changed, preventing the dirty flag from being set and keeping the modifications from synchronizing to clients. + +--- + +## Example of Triggering Code +```csharp +using Mirage; +using Mirage.Collections; + +public struct PlayerData +{ + public int health; +} + +public class Player : NetworkBehaviour +{ + public readonly SyncList playerList = new SyncList(); + + public void DamagePlayer(int index, int damage) + { + // Warning: Direct mutation of elements inside playerList is not supported because changes cannot be tracked. + playerList[index].health -= damage; + } +} +``` + +--- + +## How to Resolve + +Retrieve the element, perform the modification, and then assign the modified element back to the collection via the indexer. This ensures the collection's change tracking is triggered. + +```csharp +using Mirage; +using Mirage.Collections; + +public struct PlayerData +{ + public int health; +} + +public class Player : NetworkBehaviour +{ + public readonly SyncList playerList = new SyncList(); + + public void DamagePlayer(int index, int damage) + { + // Correct: Modifying the element and setting it back, triggering the index setter + var data = playerList[index]; + data.health -= damage; + playerList[index] = data; + } +} +``` diff --git a/doc/docs/analyzers/MIRAGE1004.md b/doc/docs/analyzers/MIRAGE1004.md new file mode 100644 index 00000000000..06f9d5faf94 --- /dev/null +++ b/doc/docs/analyzers/MIRAGE1004.md @@ -0,0 +1,48 @@ +# MIRAGE1004: Reassignment of SyncObject Fields + +## The Problem +A field implementing `ISyncObject` (such as `SyncList`, `SyncDictionary`, `SyncHashSet`, etc.) is reassigned, or is not marked `readonly`. + +Fields implementing `ISyncObject` must be initialized once when the class is constructed and cannot be reassigned during the lifecycle of the object. Reassigning these fields breaks Mirage's post-processing code weaving and prevents proper dirty-tracking and delta serialization. + +--- + +## Example of Triggering Code +```csharp +using Mirage; +using Mirage.Collections; + +public class Player : NetworkBehaviour +{ + // Error: ISyncObject field 'playerList' must be marked readonly and cannot be reassigned + public SyncList playerList = new SyncList(); + + public void ResetList() + { + playerList = new SyncList(); + } +} +``` + +--- + +## How to Resolve + +Mark the field as `readonly` to ensure it cannot be reassigned. To clear or reset the collection, use the collection's `.Clear()` method instead of instantiating a new object. + +```csharp +using Mirage; +using Mirage.Collections; + +public class Player : NetworkBehaviour +{ + // Correct: Marked as readonly + public readonly SyncList playerList = new SyncList(); + + public void ResetList() + { + // Correct: Clear the list instead of reassigning it + playerList.Clear(); + } +} +``` diff --git a/doc/docs/analyzers/MIRAGE1202.md b/doc/docs/analyzers/MIRAGE1202.md new file mode 100644 index 00000000000..b308724914e --- /dev/null +++ b/doc/docs/analyzers/MIRAGE1202.md @@ -0,0 +1,46 @@ +# MIRAGE1202: Pass-by-Reference Modifiers in RPCs + +## The Problem +An RPC method contains parameters with `ref` or `out` parameter modifiers. + +RPCs (Remote Procedure Calls) serialize arguments and send them over the network. Pass-by-reference modifiers (`ref` or `out`) imply that the method can modify the argument and pass the changes back to the caller in-place, which is impossible over a one-way network serialization boundary. + +--- + +## Example of Triggering Code +```csharp +using Mirage; + +public class Player : NetworkBehaviour +{ + // Error: ServerRpc method 'CmdTakeDamage' cannot have ref/out parameters + [ServerRpc] + public void CmdTakeDamage(ref int health) + { + health -= 10; + } +} +``` + +--- + +## How to Resolve + +Pass parameters by value. If you need to communicate updated state back to the caller, either use an asynchronous RPC with a `UniTask` return value or update a synchronized property (such as a `[SyncVar]`). + +```csharp +using Mirage; + +public class Player : NetworkBehaviour +{ + [SyncVar] + public int Health { get; set; } + + // Correct: Pass by value and synchronize via SyncVar + [ServerRpc] + public void CmdTakeDamage(int damage) + { + Health -= damage; + } +} +``` diff --git a/doc/docs/analyzers/MIRAGE1203.md b/doc/docs/analyzers/MIRAGE1203.md new file mode 100644 index 00000000000..33112d54df4 --- /dev/null +++ b/doc/docs/analyzers/MIRAGE1203.md @@ -0,0 +1,43 @@ +# MIRAGE1203: Static RPC Methods + +## The Problem +An RPC method decorated with `[ServerRpc]` or `[ClientRpc]` is declared as `static`. + +RPC methods must execute on a specific instance of a `NetworkBehaviour` on a specific `GameObject` so that Mirage knows which network identity the message is targeted at. Static methods lack an instance context (`this`), making it impossible to route the message to the correct network object. + +--- + +## Example of Triggering Code +```csharp +using Mirage; + +public class Player : NetworkBehaviour +{ + // Error: ServerRpc method 'CmdSpawnGlobal' must not be static + [ServerRpc] + public static void CmdSpawnGlobal() + { + // Static context has no NetworkIdentity + } +} +``` + +--- + +## How to Resolve + +Remove the `static` modifier from the RPC method declaration so it runs within the instance context of a spawned `NetworkBehaviour`. + +```csharp +using Mirage; + +public class Player : NetworkBehaviour +{ + // Correct: Instance method has access to the NetworkBehaviour state + [ServerRpc] + public void CmdSpawn() + { + // Normal instance context + } +} +``` diff --git a/doc/docs/analyzers/MIRAGE1204.md b/doc/docs/analyzers/MIRAGE1204.md new file mode 100644 index 00000000000..e78dacd9279 --- /dev/null +++ b/doc/docs/analyzers/MIRAGE1204.md @@ -0,0 +1,60 @@ +# MIRAGE1204: Invalid ClientRpc Target Configurations + +## The Problem +A `[ClientRpc]` target configuration is invalid for one of the following reasons: +1. The method return type is `UniTask` or `UniTask` (it returns values) but the target is configured as `RpcTarget.Observers`. +2. The target is set to `RpcTarget.Player` but the first parameter of the method is not an `INetworkPlayer` (or `NetworkConnection`) to specify the recipient. + +Broadcast RPCs (where the target is `Observers`) cannot collect return values since multiple clients would respond. Returning values requires a single, specific destination (e.g. `RpcTarget.Owner` or `RpcTarget.Player`). Furthermore, when targeting a specific `Player`, Mirage needs to know which connection to send the RPC to, so the method's first parameter must be the player connection. + +--- + +## Example of Triggering Code +```csharp +using Mirage; +using Cysharp.Threading.Tasks; + +public class Player : NetworkBehaviour +{ + // Error: [ClientRpc] must return void when target is Observers. + [ClientRpc(target = RpcTarget.Observers)] + public UniTask RpcGetHealth() + { + return UniTask.FromResult(100); + } + + // Error: ClientRpc method with target = Player requires first parameter to be INetworkPlayer + [ClientRpc(target = RpcTarget.Player)] + public void RpcGiveItem(int itemId) + { + } +} +``` + +--- + +## How to Resolve + +1. If the RPC returns values, change the target to `RpcTarget.Owner` or `RpcTarget.Player`. +2. If the RPC targets `RpcTarget.Player`, ensure the first parameter is of type `INetworkPlayer` (or `NetworkConnection`). + +```csharp +using Mirage; +using Cysharp.Threading.Tasks; + +public class Player : NetworkBehaviour +{ + // Correct: Targeted RPC returning value to the Owner + [ClientRpc(target = RpcTarget.Owner)] + public UniTask RpcGetHealth() + { + return UniTask.FromResult(100); + } + + // Correct: First parameter is the target player connection + [ClientRpc(target = RpcTarget.Player)] + public void RpcGiveItem(INetworkPlayer targetPlayer, int itemId) + { + } +} +``` diff --git a/doc/docs/analyzers/MIRAGE1205.md b/doc/docs/analyzers/MIRAGE1205.md new file mode 100644 index 00000000000..af5901cf17c --- /dev/null +++ b/doc/docs/analyzers/MIRAGE1205.md @@ -0,0 +1,46 @@ +# MIRAGE1205: Invalid RateLimit Attribute Settings + +## The Problem +The `[RateLimit]` attribute contains invalid configurations. This includes: +1. `Interval` is less than or equal to zero. +2. `Refill` is less than or equal to zero. +3. `MaxTokens` is less than or equal to zero, or is less than the `Refill` rate. + +Rate limiting buckets require positive numbers for intervals, refill rates, and max tokens to correctly configure token replenishment cycles. If any of these values are zero or negative, or if `MaxTokens` is set to a value less than `Refill`, the rate limiting logic will fail to function or cause infinite loops/resource starvation on the server. + +--- + +## Example of Triggering Code +```csharp +using Mirage; + +public class Player : NetworkBehaviour +{ + // Error: RateLimit interval must be greater than zero, and MaxTokens must be >= Refill + [ServerRpc] + [RateLimit(Interval = -0.5f, Refill = 10, MaxTokens = 5)] + public void CmdSpammyAction() + { + } +} +``` + +--- + +## How to Resolve + +Correct the parameters of the `[RateLimit]` attribute to ensure they are positive, valid values. Ensure `MaxTokens` is at least equal to the `Refill` value. + +```csharp +using Mirage; + +public class Player : NetworkBehaviour +{ + // Correct: Positive interval and MaxTokens >= Refill + [ServerRpc] + [RateLimit(Interval = 1.0f, Refill = 10, MaxTokens = 20)] + public void CmdSpammyAction() + { + } +} +``` diff --git a/doc/docs/analyzers/MIRAGE1206.md b/doc/docs/analyzers/MIRAGE1206.md new file mode 100644 index 00000000000..7ec3035e442 --- /dev/null +++ b/doc/docs/analyzers/MIRAGE1206.md @@ -0,0 +1,42 @@ +# MIRAGE1206: Missing RateLimit on ServerRpc + +## The Problem +A `[ServerRpc]` method is declared without a `[RateLimit]` attribute. + +To prevent denial of service (DoS) attacks, server CPU strain, and memory bloat from client RPC spam, it is highly recommended to apply a `[RateLimit]` attribute to every `[ServerRpc]` method. Without a rate limit, a malicious client could flood the server with requests, leading to server performance degradation or player disconnects. + +--- + +## Example of Triggering Code +```csharp +using Mirage; + +public class Player : NetworkBehaviour +{ + // Warning: ServerRpc 'CmdFireWeapon' should have a [RateLimit] attribute to prevent spam + [ServerRpc] + public void CmdFireWeapon() + { + } +} +``` + +--- + +## How to Resolve + +Add a `[RateLimit]` attribute to the `[ServerRpc]` method with appropriate parameters for the expected rate of call. + +```csharp +using Mirage; + +public class Player : NetworkBehaviour +{ + // Correct: ServerRpc decorated with [RateLimit] to throttle client requests + [ServerRpc] + [RateLimit(Interval = 0.2f, Refill = 5, MaxTokens = 10)] + public void CmdFireWeapon() + { + } +} +``` diff --git a/doc/docs/analyzers/MIRAGE1302.md b/doc/docs/analyzers/MIRAGE1302.md new file mode 100644 index 00000000000..d0492c31dd9 --- /dev/null +++ b/doc/docs/analyzers/MIRAGE1302.md @@ -0,0 +1,38 @@ +# MIRAGE1302: Field Type Serialization Validation + +## The Problem +A field or property in a class/struct marked with `[NetworkMessage]`, or a parameter in a method marked with `[ServerRpc]` or `[ClientRpc]`, uses a type that Mirage does not know how to serialize, and no custom writer/reader has been registered or generated for it. + +Mirage uses compile-time IL weaving to generate serialization code for NetworkMessages and RPCs. If a field or parameter type is not a primitive type, an existing supported type, or a type that can be auto-weaved (like a simple struct/class with only serializable fields), and there are no custom `NetworkWriter` or `NetworkReader` extension methods for it, the Weaver will fail because it cannot serialize the data. + +--- + +## Example of Triggering Code +```csharp +using Mirage; +using System.Threading; + +[NetworkMessage] +public struct StartSessionMessage +{ + // Error: Field type 'Thread' is not serializable by Mirage. + public Thread executionThread; +} +``` + +--- + +## How to Resolve + +Ensure all fields are of serializable types. If you need to send a custom type, make sure it is a struct/class with only serializable fields, or implement custom `Write` and `Read` extension methods for the custom type so that Mirage knows how to serialize it. + +```csharp +using Mirage; + +[NetworkMessage] +public struct StartSessionMessage +{ + // Correct: Pass a serializable identifier instead of the raw thread object + public string threadName; +} +``` diff --git a/doc/docs/analyzers/MIRAGE1303.md b/doc/docs/analyzers/MIRAGE1303.md new file mode 100644 index 00000000000..29c845a4101 --- /dev/null +++ b/doc/docs/analyzers/MIRAGE1303.md @@ -0,0 +1,62 @@ +# MIRAGE1303: Mismatched Custom Serialization Methods + +## The Problem +A custom serializer signature does not match the expected pattern, or a custom reader is missing for a custom writer (or vice versa). + +When writing custom serialization for a type, Mirage requires both extension methods to be defined with specific signatures: +- **Writer:** `public static void WriteMyType(this NetworkWriter writer, MyType value)` +- **Reader:** `public static MyType ReadMyType(this NetworkReader reader)` + +If only one of the methods is defined, or if the parameter/return types do not exactly match the type, Mirage cannot pair them up, causing serialization to fail at compile-time. + +--- + +## Example of Triggering Code +```csharp +using Mirage; +using Mirage.Serialization; + +public struct CustomType +{ + public int value; +} + +public static class CustomSerialization +{ + // Error: Custom writer defined but matching custom reader is missing + public static void WriteCustomType(this NetworkWriter writer, CustomType value) + { + writer.WritePackedInt32(value.value); + } +} +``` + +--- + +## How to Resolve + +Provide a matching reader or writer method with the correct signature. Ensure that the type being read and written is exactly the same. + +```csharp +using Mirage; +using Mirage.Serialization; + +public struct CustomType +{ + public int value; +} + +public static class CustomSerialization +{ + // Correct: Both writer and reader are defined with matching signatures + public static void WriteCustomType(this NetworkWriter writer, CustomType value) + { + writer.WritePackedInt32(value.value); + } + + public static CustomType ReadCustomType(this NetworkReader reader) + { + return new CustomType { value = reader.ReadPackedInt32() }; + } +} +``` diff --git a/doc/docs/analyzers/MIRAGE1401.md b/doc/docs/analyzers/MIRAGE1401.md new file mode 100644 index 00000000000..f93fdaaedf2 --- /dev/null +++ b/doc/docs/analyzers/MIRAGE1401.md @@ -0,0 +1,50 @@ +# MIRAGE1401: Accessing Network State in Awake/Start + +## The Problem +Reading or writing network states (such as `IsServer`, `IsClient`, `HasAuthority`, or `SyncVar` values) inside Unity's lifecycle methods `Awake` or `Start`. + +Unity's `Awake` and `Start` methods are called during GameObject initialization. At this point, Mirage's network identity is not yet spawned or initialized, meaning properties like `IsServer`, `IsClient`, and authority states are not set, and `SyncVars` have not been initialized with their network values. Accessing them inside `Awake` or `Start` results in incorrect behavior, default values, or race conditions. + +--- + +## Example of Triggering Code +```csharp +using Mirage; + +public class PlayerHealth : NetworkBehaviour +{ + [SyncVar] + public int Health { get; set; } + + private void Start() + { + // Warning: Accessing Network State (IsServer/SyncVar) in Start + if (IsServer) + { + Health = 100; + } + } +} +``` + +--- + +## How to Resolve + +Override `OnStartServer`, `OnStartClient`, `OnStartLocalPlayer`, or `OnStartAuthority` to run network initialization code when the network state is fully ready. + +```csharp +using Mirage; + +public class PlayerHealth : NetworkBehaviour +{ + [SyncVar] + public int Health { get; set; } + + // Correct: Run server initialization when the network server has started + public override void OnStartServer() + { + Health = 100; + } +} +``` diff --git a/doc/docs/analyzers/MIRAGE1402.md b/doc/docs/analyzers/MIRAGE1402.md new file mode 100644 index 00000000000..148f64fdbb0 --- /dev/null +++ b/doc/docs/analyzers/MIRAGE1402.md @@ -0,0 +1,64 @@ +# MIRAGE1402: Missing base Call in OnSerialize/OnDeserialize + +## The Problem +Overriding `OnSerialize` or `OnDeserialize` in a derived `NetworkBehaviour` class without calling `base.OnSerialize` or `base.OnDeserialize`. + +Derived classes that inherit from another `NetworkBehaviour` which has its own synchronized state must call the base implementation. Failing to call the base method prevents the base class's properties and SyncVars from being serialized or deserialized, leading to out-of-sync states between the server and clients. + +--- + +## Example of Triggering Code +```csharp +using Mirage; +using Mirage.Serialization; + +public class BasePlayer : NetworkBehaviour +{ + [SyncVar] + public string PlayerName { get; set; } +} + +public class HeroPlayer : BasePlayer +{ + [SyncVar] + public int HeroId { get; set; } + + // Warning: Overriding OnSerialize without calling base.OnSerialize + public override bool OnSerialize(NetworkWriter writer, bool initialState) + { + writer.WritePackedInt32(HeroId); + return true; + } +} +``` + +--- + +## How to Resolve + +Add the call to `base.OnSerialize` or `base.OnDeserialize` inside the overridden method and combine its return value with the derived class's serialization status. + +```csharp +using Mirage; +using Mirage.Serialization; + +public class BasePlayer : NetworkBehaviour +{ + [SyncVar] + public string PlayerName { get; set; } +} + +public class HeroPlayer : BasePlayer +{ + [SyncVar] + public int HeroId { get; set; } + + // Correct: Calls base.OnSerialize and combines dirty states + public override bool OnSerialize(NetworkWriter writer, bool initialState) + { + bool baseDirty = base.OnSerialize(writer, initialState); + writer.WritePackedInt32(HeroId); + return baseDirty || true; + } +} +``` diff --git a/doc/docs/analyzers/MIRAGE1501.md b/doc/docs/analyzers/MIRAGE1501.md new file mode 100644 index 00000000000..691ed07383f --- /dev/null +++ b/doc/docs/analyzers/MIRAGE1501.md @@ -0,0 +1,38 @@ +# MIRAGE1501: Network Message Exceeds Safe MTU + +## The Problem +A `[NetworkMessage]` struct/class has a static or maximum serialized size that exceeds the safe Maximum Transmission Unit (MTU) of the transport layer (typically 1200 - 1400 bytes). + +If a single message size exceeds the MTU, it must be fragmented at the transport or IP layer. IP fragmentation increases packet loss rates, latency, and connection instability. Designing messages that stay within the safe MTU boundary improves network reliability and performance. + +--- + +## Example of Triggering Code +```csharp +using Mirage; + +[NetworkMessage] +public struct HugeMessage +{ + // Warning: Array size and primitives exceed the safe MTU threshold + public byte[] largeBuffer; // e.g. filled with 2048 bytes of data +} +``` + +--- + +## How to Resolve + +Break large messages down into smaller chunks, use compression, or send raw bulk data using a streaming/chunking API instead of a single massive NetworkMessage. + +```csharp +using Mirage; + +[NetworkMessage] +public struct ChunkMessage +{ + public int chunkIndex; + // Correct: Small buffer sizes that fit comfortably within a single MTU packet + public byte[] smallBuffer; // e.g. limited to 512 bytes per chunk +} +``` diff --git a/doc/docs/analyzers/MIRAGE1502.md b/doc/docs/analyzers/MIRAGE1502.md new file mode 100644 index 00000000000..5c660c6c73b --- /dev/null +++ b/doc/docs/analyzers/MIRAGE1502.md @@ -0,0 +1,39 @@ +# MIRAGE1502: Unbounded String or Collection + +## The Problem +A network message field or RPC parameter contains a `string`, `List`, `T[]` array, or other collection without specifying a maximum size/length limit. + +Allowing unbounded strings or collections in network messages introduces security risks (such as memory exhaustion attacks, out-of-memory crashes, or denial of service) if a client sends an extremely large payload. + +--- + +## Example of Triggering Code +```csharp +using Mirage; + +[NetworkMessage] +public struct ChatMessage +{ + // Warning: Unbounded string can be exploited to send megabytes of text + public string text; +} +``` + +--- + +## How to Resolve + +Use size-limiting attributes (such as `[BitCount]` or other string/collection size limiters) to restrict the collection size at serialization time, or enforce maximum limits during deserialization (such as setting `MaxDeltaCount` or `MaxElements` on SyncObjects). + +```csharp +using Mirage; +using Mirage.Serialization; + +[NetworkMessage] +public struct ChatMessage +{ + // Correct: Restrict the maximum string length using BitCount or other validation attributes + [BitCount(8)] + public string text; +} +``` diff --git a/doc/docs/analyzers/MIRAGE1503.md b/doc/docs/analyzers/MIRAGE1503.md new file mode 100644 index 00000000000..060023640fe --- /dev/null +++ b/doc/docs/analyzers/MIRAGE1503.md @@ -0,0 +1,46 @@ +# MIRAGE1503: High Bit-Overhead Primitive Type + +## The Problem +A primitive type (like `int`, `uint`, `long`, `ulong`, `float`, or `double`) is used in a `[SyncVar]`, RPC parameter, or `[NetworkMessage]` field without any bit-packing, compression, or range-limiting attributes. + +Standard uncompressed primitives write their full bit-width (e.g. 32 bits for `int`/`float`, 64 bits for `long`/`double`) onto the network buffer, even if the runtime values are small or do not require that level of precision. Over time, this leads to unnecessary bandwidth consumption. + +--- + +## Example of Triggering Code +```csharp +using Mirage; + +public class Player : NetworkBehaviour +{ + // Warning: 'Health' uses uncompressed int which has high bit-overhead. + [SyncVar] + public int Health { get; set; } + + // Warning: 'PlayerScale' uses uncompressed float which has high bit-overhead. + [SyncVar] + public float PlayerScale { get; set; } +} +``` + +--- + +## How to Resolve + +Decorate the fields/properties with appropriate compression attributes like `[BitCount]`, `[VarInt]`, `[FloatPack]`, or `[BitCountFromRange]` to minimize the serialized bit size. + +```csharp +using Mirage; +using Mirage.Serialization; + +public class Player : NetworkBehaviour +{ + // Correct: Restrict Health to 7 bits (0-127 range) + [SyncVar, BitCount(7)] + public int Health { get; set; } + + // Correct: Compress float with a defined range and precision + [SyncVar, FloatPack(-10f, 10f, 0.01f)] + public float PlayerScale { get; set; } +} +``` diff --git a/doc/docs/analyzers/index.md b/doc/docs/analyzers/index.md new file mode 100644 index 00000000000..30461eefbf0 --- /dev/null +++ b/doc/docs/analyzers/index.md @@ -0,0 +1,112 @@ +# Mirage Roslyn Analyzers + +Mirage uses Roslyn Analyzers to provide compile-time validation for network code, replacing or augmenting post-compilation IL weaving diagnostics. By catching configuration errors, unsafe serialization patterns, and improper API usage directly in your IDE during compilation, analyzers shorten the feedback loop, prevent runtime exceptions, and help write secure, high-performance multiplayer code. + +--- + +## Rule Summary Table + +| Rule ID | Name | Severity | Short Summary | +| --- | --- | --- | --- | +| [MIRAGE1001](MIRAGE1001.md) | SyncVar Class Warning | Warning | Warns against using class types for `[SyncVar]` properties due to allocations and change-tracking limitations. | +| [MIRAGE1002](MIRAGE1002.md) | SyncVar Auto-Property Error | Error | Ensures `[SyncVar]` properties are non-static auto-properties with both getter and setter. | +| [MIRAGE1003](MIRAGE1003.md) | Direct Mutation of SyncCollection Elements | Warning | Flags direct modification of elements within SyncCollections because the changes cannot be detected or synced. | +| [MIRAGE1004](MIRAGE1004.md) | Reassignment of SyncObject Fields | Error | Restricts reassignment of fields implementing `ISyncObject` (like `SyncList`), requiring them to be marked `readonly`. | +| [MIRAGE1101](MIRAGE1101.md) | Misplaced Network Attribute Error | Error | Prevents Mirage network attributes from being declared inside classes that do not inherit from `NetworkBehaviour`. | +| [MIRAGE1201](MIRAGE1201.md) | RPC Signature Error | Error | Disallows generic parameters on RPC methods and enforces valid return types (`void`, `UniTask`, or `UniTask`). | +| [MIRAGE1202](MIRAGE1202.md) | Pass-by-Reference Modifiers in RPCs | Error | Prohibits `ref` or `out` modifiers on RPC parameters since they cannot be serialized over a one-way boundary. | +| [MIRAGE1203](MIRAGE1203.md) | Static RPC Methods | Error | Disallows declaring RPC methods as `static` to preserve the `NetworkBehaviour` instance context. | +| [MIRAGE1204](MIRAGE1204.md) | Invalid ClientRpc Target Configurations | Error | Validates `[ClientRpc]` target settings, ensuring correct return types and connection parameters. | +| [MIRAGE1205](MIRAGE1205.md) | Invalid RateLimit Attribute Settings | Error | Enforces valid positive configurations for interval, refill, and max tokens inside `[RateLimit]` attributes. | +| [MIRAGE1206](MIRAGE1206.md) | Missing RateLimit on ServerRpc | Warning | Recommends decorating `[ServerRpc]` methods with `[RateLimit]` to prevent server denial of service (DoS) attacks. | +| [MIRAGE1301](MIRAGE1301.md) | Message or RPC Class Warning | Warning | Warns about class types used inside network messages or RPC parameters because they cause GC allocations. | +| [MIRAGE1302](MIRAGE1302.md) | Field Type Serialization Validation | Error | Confirms all fields in network messages/RPCs are serializable by Mirage or have registered custom serializers. | +| [MIRAGE1303](MIRAGE1303.md) | Mismatched Custom Serialization Methods | Error | Requires custom serializers to contain matching, properly-signed reader and writer extension methods. | +| [MIRAGE1401](MIRAGE1401.md) | Accessing Network State in Awake/Start | Warning | Warns against accessing network states like `IsServer` or `SyncVars` during early Unity lifecycle phases. | +| [MIRAGE1402](MIRAGE1402.md) | Missing base Call in OnSerialize/OnDeserialize | Warning | Ensures overriding `OnSerialize` or `OnDeserialize` in derived classes calls the base implementation. | +| [MIRAGE1501](MIRAGE1501.md) | Network Message Exceeds Safe MTU | Warning | Warns when a message exceeds the safe Maximum Transmission Unit (MTU) to prevent IP fragmentation. | +| [MIRAGE1502](MIRAGE1502.md) | Unbounded String or Collection | Warning | Warns about unbounded strings/collections in network messages that could trigger memory exploitation. | +| [MIRAGE1503](MIRAGE1503.md) | High Bit-Overhead Primitive Type | Warning | Recommends bit-packing/compression attributes on primitive types to optimize network bandwidth. | + +--- + +## Detailed Rule Breakdown + +### Group 1: SyncVars & SyncObjects + +#### [MIRAGE1001: SyncVar Class Warning](MIRAGE1001.md) +Using class types inside properties decorated with `[SyncVar]` triggers this warning. Reference types cause heap allocations during deserialization and do not support automatic modification detection because their reference does not change. To resolve this, convert the class to a struct or use `[WeaverSafeClass]` with custom serialization. + +#### [MIRAGE1002: SyncVar Auto-Property Error](MIRAGE1002.md) +Properties marked with `[SyncVar]` must be declared as non-static automatic properties with both getter and setter. Mirage's post-processing IL Weaver requires this structure to successfully inject dirty-tracking and sync hook calls. Correct this by removing custom backing fields and declaring the property as a standard auto-property. + +#### [MIRAGE1003: Direct Mutation of SyncCollection Elements](MIRAGE1003.md) +Modifying the properties of an element inside a `SyncList` or `SyncDictionary` directly (without setting it back) prevents Mirage from triggering change tracking. Because structs are value types, mutating them directly only changes a local copy. To resolve this, retrieve the element, modify it, and assign it back using the collection indexer. + +#### [MIRAGE1004: Reassignment of SyncObject Fields](MIRAGE1004.md) +Fields implementing `ISyncObject` (such as `SyncList` or `SyncHashSet`) must be declared as `readonly` and must not be reassigned after construction. Reassigning these fields breaks internal Weaver injection and delta synchronization. To reset the collection, use the collection's `.Clear()` method instead of creating a new instance. + +--- + +### Group 2: NetworkBehaviour & Attribute Placement + +#### [MIRAGE1101: Misplaced Network Attribute Error](MIRAGE1101.md) +Mirage-specific attributes like `[SyncVar]`, `[Server]`, `[Client]`, or RPC attributes are only valid within classes inheriting from `NetworkBehaviour`. Placing these on methods, properties, or fields of a regular `MonoBehaviour` or plain C# class will cause a compile-time error. Fix this by ensuring the target class inherits from `NetworkBehaviour`. + +--- + +### Group 3: Remote Procedure Calls + +#### [MIRAGE1201: RPC Signature Error](MIRAGE1201.md) +RPC methods (decorated with `[ServerRpc]` or `[ClientRpc]`) cannot be generic and must return `void`, `UniTask`, or `UniTask`. Non-generic signatures and approved return wrappers are mandatory for the Weaver to generate remote routing code. Resolve this by removing generic type parameters and correcting the return type. + +#### [MIRAGE1202: Pass-by-Reference Modifiers in RPCs](MIRAGE1202.md) +Using `ref` or `out` modifiers on RPC method parameters is prohibited because pass-by-reference semantics cannot span a one-way network serialization boundary. All RPC arguments must be passed by value. To share modified state back to the caller, use an async RPC returning `UniTask` or a synchronized `[SyncVar]` property. + +#### [MIRAGE1203: Static RPC Methods](MIRAGE1203.md) +Declaring an RPC method as `static` causes a compile error because Mirage needs an instance context (`NetworkBehaviour`) to determine the target object identity. Remove the `static` modifier to run the RPC within the instance context of a spawned GameObject. + +#### [MIRAGE1204: Invalid ClientRpc Target Configurations](MIRAGE1204.md) +This rule validates that `[ClientRpc]` target configurations are logically sound. For example, returning values (`UniTask`) is invalid when targeting `RpcTarget.Observers`, and targeting `RpcTarget.Player` requires the first parameter to be an `INetworkPlayer`. Correct the target configuration or adjust the method parameters to resolve the error. + +#### [MIRAGE1205: Invalid RateLimit Attribute Settings](MIRAGE1205.md) +The `[RateLimit]` attribute settings are validated to ensure they configure positive, non-zero values for `Interval`, `Refill`, and `MaxTokens`. Additionally, `MaxTokens` must be greater than or equal to the `Refill` rate to prevent logic loops or server starvation. Update the attribute parameters with valid positive configurations. + +#### [MIRAGE1206: Missing RateLimit on ServerRpc](MIRAGE1206.md) +To protect servers against client RPC spamming and potential denial of service (DoS) attacks, all `[ServerRpc]` methods should be protected with a `[RateLimit]` attribute. This warning highlights unprotected methods. Resolve this by applying a `[RateLimit]` attribute specifying appropriate timing and capacity for the method. + +--- + +### Group 4: Serialization + +#### [MIRAGE1301: Message or RPC Class Warning](MIRAGE1301.md) +Using class types as fields in a `[NetworkMessage]` or as arguments/return types in RPCs triggers a warning due to garbage collection allocations during deserialization. Standard collections like `List` are ignored, but custom classes should be converted to structs. Alternatively, you can use `[WeaverSafeClass]` with custom read/write extension methods to suppress the warning. + +#### [MIRAGE1302: Field Type Serialization Validation](MIRAGE1302.md) +All fields in a `[NetworkMessage]` or parameters in RPCs must be serializable by Mirage. If a type cannot be automatically serialized by the Weaver and lacks registered custom read/write extension methods, compile-time errors occur. Make sure fields use serializable types or implement custom `NetworkWriter` and `NetworkReader` extensions. + +#### [MIRAGE1303: Mismatched Custom Serialization Methods](MIRAGE1303.md) +Custom serialization requires registering both a writer extension method and a reader extension method with matching signatures. If one of them is missing or has signature differences, Mirage cannot pair them up, causing compilation failure. To resolve this, ensure both matching methods are fully defined. + +--- + +### Group 5: Lifecycles & API Safety + +#### [MIRAGE1401: Accessing Network State in Awake/Start](MIRAGE1401.md) +Accessing network properties like `IsServer`, `IsClient`, or authority states in Unity's standard `Awake` or `Start` lifecycle methods is unsafe because the network identity is not yet spawned. This leads to incorrect initialization or race conditions. Override `OnStartServer`, `OnStartClient`, or other network-specific start callbacks instead. + +#### [MIRAGE1402: Missing base Call in OnSerialize/OnDeserialize](MIRAGE1402.md) +Derived classes overriding custom `OnSerialize` or `OnDeserialize` methods must call their base class implementations if the base class also synchronizes state. Failing to call the base method prevents base `SyncVars` and properties from synchronizing properly. Fix this by calling the base method and combining their return values. + +--- + +### Group 6: Performance & Size Estimation + +#### [MIRAGE1501: Network Message Exceeds Safe MTU](MIRAGE1501.md) +If a network message's maximum serialized size exceeds the safe Maximum Transmission Unit (MTU) threshold (typically 1200 - 1400 bytes), this warning is triggered. Large packets require IP fragmentation, which dramatically increases network packet loss. To resolve, compress data fields or split large payloads across multiple chunk messages. + +#### [MIRAGE1502: Unbounded String or Collection](MIRAGE1502.md) +Declaring string or collection fields in network messages without a size limit introduces security risks, allowing malicious clients to cause memory exhaustion on the server. To resolve, use validation attributes like `[BitCount]` to limit serialization size, or define max sizes on collections. + +#### [MIRAGE1503: High Bit-Overhead Primitive Type](MIRAGE1503.md) +Using uncompressed primitive types (such as standard `int`, `long`, or `float`) inside `[SyncVar]` properties or network message fields consumes unnecessary bandwidth. This warning suggests using compression attributes to minimize serialized bit sizes. To resolve this, apply attributes such as `[BitCount]`, `[VarInt]`, or `[FloatPack]`. From 3a02978be1d749a397b7c43f663c251ab20cd646 Mon Sep 17 00:00:00 2001 From: James Frowen Date: Sun, 31 May 2026 17:54:05 +0100 Subject: [PATCH 19/41] feat: more c# Analyzers --- Assets/Mirage/Analyzers/Mirage.Analyzers.dll | Bin 18944 -> 48640 bytes .../AwakeStartNetworkStateTests.cs | 185 +++++++++++ Mirage.Analyzers.Tests/DirectMutationTests.cs | 313 ++++++++++++++++++ .../FieldSerializationTests.cs | 311 +++++++++++++++++ .../MismatchedSerializerTests.cs | 153 +++++++++ .../MtuSizeEstimationTests.cs | 135 ++++++++ .../OnSerializeBaseCallTests.cs | 199 +++++++++++ .../RateLimitSettingsTests.cs | 192 +++++++++++ .../RpcClientTargetTests.cs | 213 ++++++++++++ Mirage.Analyzers.Tests/RpcPassByRefTests.cs | 177 ++++++++++ Mirage.Analyzers.Tests/RpcStaticTests.cs | 114 +++++++ .../ServerRpcRateLimitMissingTests.cs | 119 +++++++ .../SyncObjectReassignmentTests.cs | 179 ++++++++++ Mirage.Analyzers.Tests/SyncVarClassTests.cs | 131 ++++++++ .../UnboundedCollectionTests.cs | 124 +++++++ .../UncompressedPrimitiveTests.cs | 178 ++++++++++ Mirage.Analyzers.Tests/VerifyCS.cs | 35 ++ Mirage.Analyzers/.gitignore | 2 + Mirage.Analyzers/Mirage.Analyzers.csproj | 18 + Mirage.Analyzers/Mirage.Analyzers.sln | 31 ++ Mirage.Analyzers/MirageAnalyzer.Lifecycles.cs | 152 +++++++++ .../MirageAnalyzer.Performance.cs | 223 +++++++++++++ Mirage.Analyzers/MirageAnalyzer.Rpcs.cs | 118 +++++++ .../MirageAnalyzer.Serialization.cs | 231 +++++++++++++ Mirage.Analyzers/MirageAnalyzer.SyncVars.cs | 166 ++++++++++ Mirage.Analyzers/MirageAnalyzer.cs | 247 ++++++++++++++ Mirage.Analyzers/MirageRules.cs | 240 ++++++++++++++ Mirage.Analyzers/MirageSymbols.cs | 249 ++++++++++++++ .../NetworkBehaviourAttributeAnalyzer.cs | 115 ------- Mirage.Analyzers/RpcSignatureAnalyzer.cs | 89 ----- Mirage.Analyzers/WeaverSafeClassAnalyzer.cs | 255 -------------- 31 files changed, 4435 insertions(+), 459 deletions(-) create mode 100644 Mirage.Analyzers.Tests/AwakeStartNetworkStateTests.cs create mode 100644 Mirage.Analyzers.Tests/DirectMutationTests.cs create mode 100644 Mirage.Analyzers.Tests/FieldSerializationTests.cs create mode 100644 Mirage.Analyzers.Tests/MismatchedSerializerTests.cs create mode 100644 Mirage.Analyzers.Tests/MtuSizeEstimationTests.cs create mode 100644 Mirage.Analyzers.Tests/OnSerializeBaseCallTests.cs create mode 100644 Mirage.Analyzers.Tests/RateLimitSettingsTests.cs create mode 100644 Mirage.Analyzers.Tests/RpcClientTargetTests.cs create mode 100644 Mirage.Analyzers.Tests/RpcPassByRefTests.cs create mode 100644 Mirage.Analyzers.Tests/RpcStaticTests.cs create mode 100644 Mirage.Analyzers.Tests/ServerRpcRateLimitMissingTests.cs create mode 100644 Mirage.Analyzers.Tests/SyncObjectReassignmentTests.cs create mode 100644 Mirage.Analyzers.Tests/SyncVarClassTests.cs create mode 100644 Mirage.Analyzers.Tests/UnboundedCollectionTests.cs create mode 100644 Mirage.Analyzers.Tests/UncompressedPrimitiveTests.cs create mode 100644 Mirage.Analyzers.Tests/VerifyCS.cs create mode 100644 Mirage.Analyzers/.gitignore create mode 100644 Mirage.Analyzers/Mirage.Analyzers.csproj create mode 100644 Mirage.Analyzers/Mirage.Analyzers.sln create mode 100644 Mirage.Analyzers/MirageAnalyzer.Lifecycles.cs create mode 100644 Mirage.Analyzers/MirageAnalyzer.Performance.cs create mode 100644 Mirage.Analyzers/MirageAnalyzer.Rpcs.cs create mode 100644 Mirage.Analyzers/MirageAnalyzer.Serialization.cs create mode 100644 Mirage.Analyzers/MirageAnalyzer.SyncVars.cs create mode 100644 Mirage.Analyzers/MirageAnalyzer.cs create mode 100644 Mirage.Analyzers/MirageRules.cs create mode 100644 Mirage.Analyzers/MirageSymbols.cs delete mode 100644 Mirage.Analyzers/NetworkBehaviourAttributeAnalyzer.cs delete mode 100644 Mirage.Analyzers/RpcSignatureAnalyzer.cs delete mode 100644 Mirage.Analyzers/WeaverSafeClassAnalyzer.cs diff --git a/Assets/Mirage/Analyzers/Mirage.Analyzers.dll b/Assets/Mirage/Analyzers/Mirage.Analyzers.dll index f0914ae43ffa909acbf4d3c5ac354e4fdcebc90d..0b849add2347033c65d2a266686de87baae095ec 100644 GIT binary patch literal 48640 zcmeIb3!Ge4l`p){sj5?t?&|KU>POzG1d?>p50Z2ex=9G>yc^PV$O8xuL#4Zt6zOy| zr>a9*NYgEXgNheabWm|1%FJ*@ML|D79iKSn8<`Qsnb8j!@G;~2@w@89QNsQI*FLAt zsp<+3Kkw!He)o3L`|P#$YwfkxUTf{WPgVC{afb>hrGof<@kOORg(rVDNcz&rB#P_i zKUSw6i9WI5Q^t-b7VJ5kE2Ky5{2@DYG(DIZ8OayZ2eN5kpsu-vRr%k0Iv`Q_xfT5mF!QWZpC==x{TQkixc7ZSsXf!mUILL;D&i}S0N(C{ zJB!)lMZl3qkf2QF>d*~He$q-^(`gs%K|n&=v&e9wu8@%B&j$SOw6nu`R3xoRIXw7` z0HtJuQX5NvlX;AM#SdvSRYofD>|^08E9Q7&(;}yEIWXLWlq6ax?rXhY(+uZDS%o!RTnszMe=N&B?XI7b++VE z%S|;O%)yirs<0Ngn3-<`1^Ff0M;hW*134rbpH;x zq|rJLKwL$~fRwS@lZl`-#&xD%Th>V%XQnF%g%0jD(@=aCBEj0@g39)@3-;Qo$R(pJ6J!)EcD7=Lk z)P&nZ-TxiEp+*O4#FK&6PCRS7{~0*Q@EicCAjPp9IY(i7KE)Pl;(TOzXL6m*3}{$La2zY~2rK`$Dlyuaq)(xmoL@gr*pqD)*16oU?tyq1rnlNN60u6QFjdo$75%X;jF-Am; zVi#J(sD`==Qj0{{=yy~I>qv(6Y@zEK1NMsWSHSjS70Vif!nUVL6zPbV1*n)sW20;2 zHv>y4XB=#Eb66`#b4Pqy}f z5YA0OxT)oqdI(t{M79?qTcpm!EO&8$|1g81W?WU;77i-Q%3p-at?*$MWV=aCKy;`m z1}T^fYBF3qWNE{*s71HZkPsEGS5a+*lf*9Rjr0s7D(2NgZ3D=%N>(hf)K(GH*U zifG^QC(+JUXQo~HdFuQ8PZq-T4O_?Gvv^z_0?3b z18sGrEl~0(v4FUqmd;rLiP^xc_ym?-Fp}S=*$3lMV|7?z9gl!_q2^Sutp$-P=A`vY zFtCF`7`~vhHt4?*Hzg3`!nDNZ$2<2qxw_5)Cs*IO#mO~vu61(B&J|8B)wyH>f)56@ zQJ6IW@wz~q40HfG4}?vDpb(_V1e4EK6NrZ3yST0pbd*cj@#l+Lv`N>?ytZCN9F{?r zLG_)0?7Otj24}*}@$Rnz7Z5ke2u*}V^v8hqIpB_2KF14Jw*i!BFHBoZ?;KGx;@vl( zmG)~4AZVlK?%M(hG3ut*kSFsE&Tk8z{DsEIFGQeVbW}Tj5kjc8-j;axBJ_&R=jlfa z9zZtWF)!X7#bZ{y+tkn>(D(d?c=vzfGac{#DV}Ej5-?0hQI=zdr(7#f57Co>(?Ki^&DjGvv0a&?bk^hEWMyRuj-zIJ^+2`I`ziR zj1;h8XRbS-3+_I3!zpR~wU0mez}k~;Yoa@^?|ud_T7US#!Dl-Ar1kUb9{FMC#odpy zAZp+3(%W%E_s2Erff6d;{Z|_GXbCmc{Z@@SRYL7+yAd7zTM53TZ5Z%ROK@M?Re=9c zf;Y770Gu#f2YGJWIVJm26vUymomK{pPG4dXF0-krq*{AEap zKu~<hk=&1NFpB4CZxL!EzPl|VhtHc(^Jk!Pog`Ek#bDey8`l; ziZo4PD)X=+;$rDauT)|H!eRjEL)*6_4%o{PlgVT&0>)VL@<3x-gNJNxh2M_Rt8&y- zBGuj~qb3ri5oxpCS$To(3<-2ojfuwg5rhm)m^LhQEY*^1k-c52xovy0S$1}*87eVj zj2<>MGcmKhDKS&_b{)0IAP|lrGGqWUD>19RF@aK`>1G41^D+C&(KY&kqm6ZZG|?1C z3>g!!W>Ucx62%3NZQI<-=UthKv`tK4wO|-ShLPRNHQ=y+tQn_+v@Td(?HfuZ zEiXG-jf}=1E0(BnMr)XbA!}eA>1B|`<_XhA{1Q?7;Bb3#GK&q zG}gH>r^7n!!5h$7gVUMpm8_)LW{c~rrJKV4_$bsg^`b`%)jZnn#8HL;G9O9o|4#iOY?i8(#3552Veq!F~ zAT7t)iL<5FPrO>`{E)-#MTtf2Th#;%+$#|4#X&WiNVjIu*3s6*VBlhxffL-CTgg^! z`m89Z=lDw!OQ^KzDnnFN+NJ1osoUo=9I0}z@nN#vZ)a%5UuK_lQ)yFU?*ZjB-IhOW zhRHgJspwr%AkvChkJbe7>1H(n;kX>B65G=)=vBL@MLKu*;ScQ@GRq#1v^V-`Kl_!^ zz5~P96E8_StGqhe*`h={JAWzKUj*&2vK98dWY@osVysmfV>kAn6OmA$y)mL4KDMLU z;fwE!M|(cPHj(@xv=Ltrms6OCgy_^4c)*_9bRCHpWIrpxLD1MQ2XODM(R?JcYy7HV;Pn~Vs#sVFj} zH-%kj4{9f(C5o|?Xy2tNPQ=bYGOCO3h4hOc)B)`WD~QIfE>YX+5*^k==QYvcGNQTW z4fuWDL%i*M+`cmZsQSxG49QcE5DBUoltr)=)8%g7Y}o)JdaR{^bpBBFpusJ$*I z;?)q>J%c#*-zTFYd_Ln2k|%^;q+`}waBdnI=W@Nu`JmoG4kl{TOYUwsvbqBq*+Kpa zO%S}+rr-L^(QDUUjvfSJrM0|-klhoCx$j&%2k&j(G|HnZC~XG*zuP)VW9hutQiXP#8>S7yu>`|;nP5er%Nd{ zH!-(;eqydoY2^!~ybjP5ge(w5!ZkfVF~6Pr|Iv$+X^;FI0(z zdRNYXwkdIzyDLA-zbj`LJ5QM?W$@+WB12e?fe;HlE+HxRx5V2*V4=G|x8yYgoK<*n zjKq%Xz;fs!h?mEoeKJGhrafi(ubOB(@|ceKV12?o8?W5)^EU&R{@XMLeIo}=I+zDsjMD={pjb2yHw~iOLXAiu5XO|8CrmR> z>@m;$t*8-eTLZh$B{Sd%Bs^+**5}bznuv- zeWl*p8j#1xzo z&t!;+Q~ht5{{a$Tb?AT4e%%}t6q$UPiBo;uT;$(_#8;iicy{AF6f9#>Wa3o+9`o1C zFRMPE)xQIr{VOJ&X{S1*`Z)7j7L-*_v-;EUS(%B53~>IJ@Q?B>hYlFPGle<6(abiLZAY zlmzX86)4Cu`2Z8A`ukQQ-_uoA9l=b{ei%6W6qA}&UiCjRKef87Iu5Xc^HaBDABne* z#D`+9DDEBCyJB8XE}AI;&B;Zx{CNAd!$ka`cf5_C!ixJ2)$SpK733*2WnWS8*o@|A+Xq#M$ z>aKlT2B&C=eQUS4WrZ!nt!%itraOre=fXMOx!%dub*^%9^_|O{TtjEt$u)L1yZ6%i zHaZ}F6Me#xuhq9S@_IwZk_k9M7&e$!3nmX<%?08U=av8(*6ugMx;gzPJ8PX>N+^Yq zt<#q>(os!YMsA)1a;zmA?Yas0AE)b@wvBQZ?G*D$q;3Lkrc`{E+)g{ibVzj*@Ft~V z-@8#kl>>M9WB&o_z&8S{Nw73%!#_A6BA1}OuLL3(3)*igfgJ?DPy*o!g7)uAAfgP| zt#jS3;Io5vrUb4acuxsL6cx0;TLQZX&hB>G;vyty=Sm>iZl5ZFWW1f~aVwMccCG}% zvt!klK(f+~pXXL4>+DPkB&+OCm%w!Z=iOe95CF@9tl_dCPq-||3@!_Dfy;u^=d$3u zxhyzgE(^}|W zErBH0exd|own4k*e77w&?m_#a638gXzPSWmNbv7VAR{3=zTR!SgW&2C*iZ245_mDd zQjE0#{59K`rXRm&&QuKiZ8XDqs7K7oq~BpE=+Ke3Ai!4tdr2%Xr2$Fd9gsyMhW zuZ-UzP0CPr3JN-rS{_y{rs6dibo~Z)6-b(T(-M(A3swC%sx$O<-#+{iByWkP>a!H6 zd(Qy1K_9qNeeJh^N%~A_E1-KyP&c5DmY`*TPL-fJfPPql>Hz)Lg}6+(oQnEf7EAjz)kZ5_Hh8_XzTAklUR@kQ>k(;DlpKj$+L-uuX|^T>do@}U*_pkr z8ZD^63BJ1NH~5HY(8S=45h~|p)TxC~HOU{WD#J4x>PB4!IW)`%pa-&%<~3$O*&{+$@LwHDBcD@cWP+Jb4Y3U>kBa{G{kR&(B;e-}W- zLdc`MaDKh76n3McH0mpb8L23ZyQPh%t(s}2HNMgomcCnvg3(31m;Mme=RNrlhT zS@37N!u-VTQse6bGO*dXFFqGp;avo3YQfUi=3f`4(oh2M$&_uCLV18%*eV_!VTFvJK0&UTN)pB|M z9$-#LQ^?Fz6KIQ`^}Pl(hJGp!MZ%1|j|Q~ugqy=C@VGbwS&mG5my;2-H(Oyv^=sJe zvz!9`K8V>0177}uHvMKC{q>-mj=%By%v)Vrf{m9jW_8FZP9taUpOKs>UMmS+;-~4P zpC;(MT0;JWPB&o-JljXt4WR4&4x9c8&)T-DK-ay8v%M)8pk z2p~o~I>tWndQ#+mW>S_M^wR9cRVZOi5y>zE6p>{gK&y`Ty5x!c&I5hv3i-9^V?DoA z8UxkjA)y5{>1s!#UdN}t@2a9xto0H)r5p2I|7$=Kwoj$IG9D;j63BlL)N3kY*8Fw( z`vKPAHCB1EinbMXMrZl$D2avDwjg%+u$?pV%g&9XqO zZ3fPV`4$}CE_M!Vwz6yQz~&i!V1xGTXB1CNUDNFJlhM}~P(vI~A`Fhr?TR|R!ah13 zuY(r+4a!n@vq68%Cx}nG1og*!EE8{8kqU98q$7@^HdOC;S`WW*_k|k~t6+5fxw~Vl zI+u5LE$>>viQ$tx!$|+w2>XtOdbmlc;aSMGdEtc}BY_yqYeVc#yh=j7689l-xIx_AlC(GTJ`fnP{b?ZfX8{9;;s zPeeZO$2#B-KU*Z9NrbWDnXt5FSS1;Dwld{ycwM?InHR06pv*^}R zJ8MqYTI!P8=Nl~bOzTkT#|9FgTCu#@kE%mQWOrMPn)W_8KYg$t=b$zlm zRi|!OSB7KiN6G%in0ly%_`IZ_t0!i%t{<0bBkJ=;Onn6Tn7Rk)Me1*p5Qgd!Ojgq0 zNqVQG_ZqDEap~a~$m6`d{^zwZ)gqY7rOsZAl2kvLajL~qH%kAH3x0i!ZNJ>iRyQ`2 z=3Axo7Sy*?$oRbRIT_tjzZ3i)tp3KN`WE_$srMw9zYO`LDhg&&FmD&kDM>#p>DHMX z_kG~1rRLQ%fs$DA{zObY7iSMIwy^XPMPA)2!5MrRR! zC3;Az-&$;YM1QcskyNJ4Wn?BtZ$aBSwRa|EIwf;SOU++kMlm%g zRIP2IR=fq&QqPWwy2)C?hZi3P_g6zZfdo_w4J1VEZ&2OP#MQ_LfoV{0PTtkD1)pmS zsVS5_mAb3xV}Mo)^fQ5$sV;$@6==O$Eoy%gy8akG_uGx08dMf*^J6$w?m^oI^=mAp z->Y2;#sSdx1lp@MsK9~-^%-e*x!SA(7;i2p{}{NoO?tTvH9w}VRQ&=?3UoC-Gu(tW zUHFX~`%!nXI=k_T=wkJw+Sf%do&2wX4ATA})47tLC;4xf#JpS5a|N?N@=r^hzmxQN zNzWDhRg&+P^dZ4KFKJQIn*_fT`8DeABST2fi!!}J^0OsBU-G{R6W=bF_e=iAg8!zZ zFG%{J;BS-sQAtCRo-gUQrSyZ6|E%N(Qisv!|D6~`x;M%68_i6Y&0=~)FmaRl7D+!6 zV*an2m@W)6t!rVrLh_%Lwl_Bt^H`keJ0$%pNk1g%{Zc2`SVZXy$#JB|o0*`bN~mNaQF-zMpJ{dly?_@FuwU7!|)rjXW3 z`pfuRkx#|$MEd!byO3(kp=btgKKwHH9`s-v_e7ha$M;1ob$!!^Q0FnodVzXMQZK(V z%{6lCjE9pJYVRvSA4N}g33>$38%hw%-Y(ErA{V6|19Z2B)RVPeikj+4f$mfv4xYm2 z7=GeF{j*L*L(p`EpF7pPp;OVYYI2}e$y3p&>U5wlMxTtyCe>d!&1)Qri(6 zvt3R2kKomV4)o4YFg8Oy?Lbe&e-WLj(u;M?t7pYxv((=@(7)6tVzbp75z?@iKI_To z9JK*K2cf$G%~gMNpc8=RslTM7L|F#Ve6Z_YH^ycQl*f#YLgA(-l`Q&h{ zPt|VG(AQ@EB6^|PDv;LCixj`n0X1hlnk>XFQg`$bdY5{z=8w^f)K@R`%KFs??7CR? zrr7nde${~82BA67iHp^cK)RO!HHtloQtwdv;wNGQ>c0wfYh+*38)7@v#~mmo&|?~s zwX#!vzXZJvH3I|e&y@P9^`6*XHE;=`TO+6Acj1=) zGJ&Qdb#?c}E>+j)GK}!ovCGtr0)0iXmwoCN0%@wRQ2$w~N#7dW#XfIF#PX5Y6{=;o zj39JgsnP=74A1q6*p=!W2l^qPtJHc2nq_<=6KjPdE%el;x6t&u|X ze&ZTdbf9+%bfbnaUW2+ueM&>PLH=Saqc$Tt$86Qj&0mcjQ2**c-){a!Y)Bp7tINKY zdM0*A{hfwngv0794#W`-tFLQFMmVf$FV*epQa_00)Nu#;anlcC*Q(Du(C+{-R@Fa- z`a%nxGgDLD*7R!fhWZD@5r+C4qHVWrAy%F@6fti9Yoz`^*Ac_KsdT;|7@c1tbv&y4 z6uNocwzB7dGnS^|E(F-_D8vMm##Hv~QvRoKz?4}8WEMd+kl={qi7Au>grC7Pj>}o- zwtoKjF|Ld(oP!ujr+)5f4rq){&u?bUU&ToSLP_+m=j-o5=iiNej$89EHYaZX8$};h zogrN+_&rizOLI-ViLpLnum`55Itfn~P`dw9qEWiOpU!`-i>illv6et;;XToZlQHZ{ zxlUF{+9PSNq$=T}1#>2^lDku2+)KKG6b)OncSh(DO zIN5{zw;L`%oOtKVU2287*t#6=#73G%@Lv4=$w|B|^L+Cis@F`n+@<==<1tG;r~V(~ zE_DiZzKrv^`;$-M^NFnUWA&NPT}Ur%`hi-dj>Lb1^!)ng)vwjxL{9^MPw*~8uv_cn z2I|C4qZRu+rGDSoWNemxc1S-v1pgd1Qu~ePRCD8yu}4a;l2XI?WYd1bFdnfcj7!Y< zGv8?3X?(Q)cGSGD@g2sHjQE_o2o0=et1C=hSn++-ZKI`9tO~xcw=l$Ae9Q=Tsf| z{G>VybL=w)!Hp-?L!j+R)z~~cP!#SJow>X)vn^m4i<|ocHwykn!S6F}uiYQ`v6=+W zR|$XKDE#@gx-9f~;Egihw~EByiV?pPc&iGw{04I@);}9~Kz+JC7<^K7C-xib(DvTo zJJI%&!S|^XGw)FEQ@7WCEx1nIGP5agoxyp%Q+n$&z6_ZbWvma%SRWL8#yl2Z5n5`z zf9747^*2I&p#{dXNOR`96YmP$XOJh4nv~H4#vAMJ4}DD1oOvSgq0pnkrHnBne!uYr z<9*HdhrVErguWg+Wvma~A9_;KZyWy^`i|hA4SieM-f8^2d9it$%nlkBYr%Zas6$YjAfN@duomRpmPaZ`59vpps zx#4fDN1XhJ8a`(&4Ghivmh~A2^R|W`q5r3ApGP_nf5Cz}Htz_3O*r$EaOP>@ryDA`EuKoh)Ds>OiZuN1b>#&om!>;gYq?^^Rk#1KO4rX?!*+_S)E`tg7hN6??&2%&j|cS z%_i(GzlSu2bWrkxPJU2@Yi|||BaH9W+^hywXYB`YSG5;u67Ow&0Ovwi)_zGaUlPps zCI5ZN2MpE>7_51gA)`osP|}+v{ea-VB2=i%4e-I(% z_eP2Nyrc$VobS~bv4VQOW`X1vNd6i`)&Eg5DVTdDea=Z))2NZLYB*M}fcyk9kf04Rc-L2Y<<;G`?ZyMh*P8)m7Ve=;Q?dF%vC(NhK z6@kIPPXp25!r=Dcf#8$DXM<}(7l*Q;$3i~|EwI*Go2&!Yo2>U(zp>2l%yp z)p_W_goZ`Yb1za8Ckr)5FUEHwn9!|yq@OOc41|AH4F}N)BSJo%3f3y0+SB0+)9}hqKO@8lLaFV{#|8p0-r0GLE6#hks zU_d@YuL~Xx_an}7&k8l}i{UFd;fi|qBM#viT?mwiz?%?g58)FbAy6L%kMQ+FPQUl7 z_hT3R4}lM=e+ndwKZKf$Ij|W2WU;+kx4vip{+0Wet95-NxneFeoO^wC{Q(EHesKT( z&AGzpaAthdaHdeGQg4Oo+cGkCG;3!L3}>%dp?U|4x%>z+eOs;@LvO|LP5Gmv89QqW zPFF4F32o054)4wuk=v0g@X>el=va~c^xAf2oQRQP*Gjc<#g?8;E6-isv$?l-Q_t4k zp5>W_T>SGn2EgS*bSXgy@Vt4&c)4Ajq56sLLtnQv3JsE@q0kK_QY%+cQ3z z-3_*#-?gf8sA-7RWVl+_pC1|<&aPMMcG|gPnPRr@=;(0vXm*4h=SMbYi<#VTVLddl zw}>ZqslM#1MyH-G$e-Z%Fx@ zl@;vF+6VLY(agwTc6;v7;Q{n_IGY(dQ=R_e*p}mi+3ZlZyb4Fn9U93L$Lwrb+a0-s z*}?I_;q1lP;`Mp^2x%*?;S_Al9?l%g<;QG~gQdx10;PWXbAgNV?#o_VI*}-8LiXn9@SI8Cd zqz3QGLI*|)m*#UruFeWy)w-=?!^0P6j%L>%*}s2dX7C8+@09F53|+5Y1e@cEdU`ud zcPMKYrWaghgPtk{Bya%J&5USbDeQ}K4gdy8e)ss%1NmXe_7xlq7W(m0#lC{GWc#zl z!}!#6UjgdxAOf@y)18lu+k(AI{jLx}6|K zSl(CIa@_XRWr>c z2ZzDR3fPz2#F}y`0j$YP=2E;2p?1Yi>tECXJnGxsyN0`@+DK|8b!fY7(?C4^>>v< zQ1|0Aej4E#J%RkjP7qW}s<}D{!!&*ggP()B?2w@LL%*@KCC_@U?NYLe`i5X%S=laC zD48%d!YP(tq>%?`r(m0)eG1(b=E3byFzM&Kgnq1Oo(rJaQjV<<=w9=7u%2&9HY z0oCU0fw4n}vi3$hf4wXzjoxzfKz0azvgFtmBh1`z))uy)J6}<$(q4u+CCKSpVof)u zQWryR<(O#{cpdZ>3fZFvhR657bNTw!u68R88pD(6^|yl1OW{kP+|vvDMkIKm=4Xbd z)dG_^QyU>$)z*;X!Q7!STdbnjx#x);Rq;Y1hjY?kS9UmaTyh1ktvgG2^`WuBqF3PZ zin@buTlNav783Q3jC%!+c@umwcOb{Dfmgb599lpv7u8h97@eyvh*S|}W!(@9iUI8? zq5C0#tX)y)?PAo9Orh8Zr9D1yP;DK~9C9u4p2JYOA?UG-a7gQPIL?widHpg8R#O$5 zGhjQVd-Bfq$bol{9Vp04D8gBU?HbRWj?8XHcH~g;u!17n#lmGdX2pA5mKi>hwKr!+ zfpInDP$*eCYf1$6k~ zK-v_#MYLNIP`C)%2sd9cYJP;&9~oxd=j8&RG#g^>t7oOP{Tg4XTc z!9gr`Ul|y^Bd^0AX{IfojH3p{3hKNgG0k@^n(_i?Pvh(h&)}Hd;7!ZYzlm{69KlvA z@>pE8)MaQ{;jZihC?5up~h9M2ApNeQG$rKQ#PHu?ggM5~%W{kcIqU&tRUVxK>h71_;%Gjrglk)rwlyWtjjC}LL-XRq*n~QHK7&*qeT6=3eul_i7+$z26!~W&VF+;EfUanO ztc-3k5k?}eTkzTxKwVW4_^VJQc8`tnoW*~dQE>UC*9wB~5~jpKL9%`C@G#E2aE5|i zSBVbSJ!w@Ik9RD`avh$)UD3FMMmdlmt+d*VN!>iP%*~g4Zix(s4ZZ>}+y}Y7o<6hy zVc>X)L6%<^by0OD!4>MbBwMwq!l4G{jCU~yj}#o)mG-m+v2w1-D{;g-A}LM6@i%ft zQbKxG+`*H_o3Q!gG-T~*J1d}@WgX`vKyAfle$!Z?m_O~RQ#0&!2;G|gJ{*@Hz@+R2)#d;85=G-nxjZ7v=IgXp^efT zJ*9gso0%&jOj5F&v^~~&?&(XLd%EjE9!jhJ{4pFg;?Id>p$IzGRoeTzkk9S9C~ZV} zvf$ckuS8rF&t74{U98VXRW|SyY4=spR5oj*ulm|^d_qxrGI*0B*ebe>kDdidfrcQ%8S!HwH_>BMnbuRCpc+T(jia8N(J z49D7METj*d77ID!K?ttr@C8avA(Ya|iT85nE8dff#iiLfqftj`yI6%z%g;DR(^E9 z>%tZCeIr@tVn(`m<900#XJ6)Xm%FpDJlLwyG2+MrC#wV;m!Z`M@Rp;aNFC?xT3y&2 z{~-x3u+}NHU_I_i_v7D2mBBsg5xmVYq7JF8cxLfd{}8@@rdM5$vLk?1&2HS&XOxXp zwG5!_yESav)en<=E(<{!*t#9(}K@ zfV(Q-Rrs>POVk)BfKfnw_LxViaCe*sRqR96d-*Q**i=43AL?U9W!Ns|V9y^!-y`^! zN1e1f4Coj}N`p&TVDuSh&2kEoEauXBwZ_F)mnwmaS+r27a;s z@*2l5Y7SCJ%RJJ!1?BwqV3YzRvJ~GnqUAwaFoWGM-_P}!0qIVoKIzph)9AGT+yLaG z<;FSM$g9cE>IoeN2_)otR4t;D$_~y4W+xb)fjHSzPvtsHEGLs<_Xj1fCpw*7>@km8 z2hctZ_Fs%=5#v#WM=JUzE?rTBLZ+a!dO2#4&|)M-$3WXK-cH|z8l+^HP^ur;<6voC zRD6U}-cs2n?Guou?vTvMRXS2m#UNX`2yFDGdi~@4Nj+?vPTQZ<&*1;Nel)MMin1gJ zT{-#Zr$Tt`N~xvgGV2E~3iy{1HPiBU%*r`0U2{};I4ZoPfl%{zK?62njPeD-GS*VF z-SX*rrOw^tEU!G|NDZdR(n~ZAoz~hwy`g>|6n&G$c?lQi0kq}9Jc?NqVYm*XElp$^ zdA(?LucRv@y-_puaylx{g43c?85NY2xA}8XQa-;kP!gr|#0=NyRm=IS@Gi^&+?G{Q z6h0lt%Ru~9@!crl+RcI&D!L7`r)g4&&BB>1)=&;5=%uM2t9KV(<=6(pz5=gpEJvya z*?t6LUW)qUQMqZ<>nbC7S`q3EHBHMmjnS~5NADU>Y0xTYnNh^3fu}1lU3*Xr3GE|AO)>^C^mLNgeucE}M4e5gIvp5=wKZ|>*m~Lpd=A#d z_^IJ2TwL^6WDMC%r6aRR)i~bIqO#IJy1u9Yri|hr%c-Kw4Wp)RF)FHB07MT%)5tQd z@OnN}Kdthvw7WK`tSBIK99)k0C%b)|w~UsIw=!g%L=O^VW)bNQ?{>tAPHfX!O>3{5t~7Ql(|Y zR$P!=Y^=*A?2bp}qk`MOa%qs`ROwMrNIB4NlIm*9zmjWuejJT*)tqyc*6o2#gHrZu zm*-xuRF|PtM;kVDMyt4|WmgM*T4OmIYAt2qQON%F@^>{(>tJ;a^t<+#AKP^TIdGl& zNL_+~tJw<J*`3 z1-u4#4*t<+u#FpD7E$XEa1`bk=GuvT9`*Wt_09nwXf|kENCW8`cOntus@mf!RvMx4 zZkOj;7Bni1W;{xI;GA5tfSvC)vNb1v>HK}}(S4&+TA-Fo~^2jrP zmHc%_@ierOcsFh-pMu8HV7d5mGp!lvXWnb4d)$Cc*GO#a#x{#i>$pVgnJa8=+9_vY4CO<#Tg%d>8JF3Xaqg;=Xz4&Jio(ufz0%n!uUXM*odPUfmNTH z{pVqkyFy&axa7Hbj^dG`1E;p2n54b(zLe}x#OBpI%n~qXXf%aALT|W3!~X21@*O$n!pMYs zWxWU1{!bew>XIu+?OVK zcDjBWOJPuPbhOMJ9Vh2T^WF}RTB;}OT5#54tga6ZxKtPvacA{%(=?{Gl~*|vU#TJ7 z&wBn#M_}~nH19mWVoI2CH6sOXM+k4nRB+2C5g21nO{U&5?f^_C440jtRrb0I)^l)L z-yYUotmSj*2Mu&4`ecyoB>kRd=$Kp|V3zyn@_t@kJ6@ah;;f8{^|Dg0mx6oZMEmM- zq0+{&Z*^Gs9rSoCTp|Jta4BkU?FfG~F3PFZ*b>`M+HQtH5=7Y`G; zOu0C@qex@(<;GrIvfNwffHjTUJksP|;}b70*K!7HTgvV^6ur9E7ur^R6vWl&zl>l! zT}cS_V)}WhHoE#?i=>j^YNHrOT&kz{!8YX-YcIi?bn7m|bid^cYjs9D2lR3_Ji0Mn zgD8ulpT1Wi7O~f&-Js0dh`O98UtDUFi!Zkk{`FdoOa6VDv9?+tXX3BFFc$8sP8qr5(wCHY2-2+L; z>u9x>lxTUJsbh%p;ExuMoGX`!KCWha;t!#Q&)4BAaM^b`oz-nsr960)&dmq=)z`hQ zzwSq$<@beYwO;Y|bC+0ReXsc1NfGj@;;hyvbv{oF^_EJ9yw$j)WB2K+d{8J&2aB& zbu8if9rrBKjV)gKTB4d5)f?{AH*M8Ii!)#OQJ+@6?ERYmu)q9Zp5YNSRNseDzC0w- zD$YG%0ly5#r*F@_@cgy9apUQpc75)_=KnsoRa$0Tt2PdAryFel+5Vq=Q#I9JZKcq| zct;A_7?^EhNjHj@v%)c6Q$#U|8Lx8o%vds=R)+kuZK%XM(I|6moYwLG!o{ngH%ChnulY4YD-N+n( zg{EoZmXX^-@4nBNe4rPF!$6m-)yo@gDv}$bR8zakJ?Hcg^wkjh+6jF6hKy=}4aa~E zZ}im~-#gHLz5Lamml8i0wtI|&Q1wuG%jw)^BAF=G$HNI@08uF)rC!A2nglVcZR8rbDl13dDQghc8w#`eh&G7Yn-YArYa= z9&hsAEL;}6HX?R}Sw+NH)mK+;PuzE?7zgRN&K2tb=3lvzvv91$Zx$6UkL&Pa*yL>k z&PIU&-;i7ZFb?*wnsTw~WqTC0{d*=Ith(gr&53)h;1N9ir6H~_Z7z%{qns31Ue#yi z%I?f_bGb4-j^@I5^~Jlrpk{gZ7nQBtV4tQzzeRNz8lM-9)c=LvVue;Ds_{nw! z!<`zB^JPq)c)@!3#jt*5=HDaM}=ko%Guki5oRqi<{$laitW=OkWy=CBD zuku|CHTmDYD3%+c@;9-nZ;kw#ruC#eEGdstNme;|+<5K+q0~x!SEWsezB*;RKr^6s zN$wP`#K%Wy1vv@!Sj5-PxrU1xqhG(G9_jlA?}H}QP783_Kl$Ejd~-?C2Yv1UJjSi$ z9JNm$tm|7~y%hK!Ctu-nmzmZr*3;p<)?x3ofXj!ARa-c1zF#{Xt8TpzGWBl9oDRu? z*J9{^C)Ah4w8IP=`OyptlkUtr(sZ{r#^f*mSLhi9#Wyx|lu5^+Ut*%xc<*mfM>42I zVU2@ay-4V$4hZplztI0tCBGW@dv}9#nuqe!W$M?>)URp;=@;_KIYn*KtCU>jnIR#q zW193au|`}J?gvn~fM{m*$s|Jqu76s+A+$gI8U@+3O3;{d7;9-O%C~mQqubtT3{xGI z^|_R_hP`}k|5Ztq+CAlcZwIHUy~&BcdwfT~OZkrH@>zw3owvPr}p3_B?f!*8Sz9SH{{IQ0`j8O2c2JxBY*5 zZ(E^fG#b1wpdrXsxn@#*ue^V~f9peE_!RVXJ>UDM^|Vp^3Y{7?x6<6`lL=22FU8HR z(i8c+6EkLyRr=fgYN+(=)yn?~eRjQ6pZi`poz*_U`4SwcSi#f|`gHF)vI^HKY+m__ z*ofBj^v6W6c@UyH=ppDW%Rimki4QgD*DCxkPgYvU^4|Mk^L4DM`_or(o<`6CPLE(Z z&+wwA+VaZzT29Ahn3s-zFuy;!7Y2v!QIu*cr{$$Sw5gVs*Da?_e+W%~bX2W)shT=m z7y%XDtHSDh`Y^_a++*-NrO(9CEs(1A*5toFid7eElGfA!8{tJK_H^9!>UruAggAO8 zuJO)_&*7>HPjsfyT|Y}bC9P^x`8;2)UA5PvE9Lldee}uO;U4F7grb(5N!u@V_{@k0 z5$G$bhX~`0tEbvZSNM=FzDxuoxlx72_`R%}d8Wuf&1UQB-e1+TYyfc=^#xiVC%hfn zA^flBtdHO~FEy0N%Q*l_)tmQyO2HjGFht&9*di3Gtq$@W-j)2N$U zXAwX7Yk5pHMVgXRAtN~z4WV?-j2aZW7%OZhTa&G!v`S9R3?jX{A#61zGe$Cte^;By zoQXfYgW7X}2|LdSF&QJJoSJBtQXt1%7Opj$5=V{Xbxnyrr1&>x0)zaxF31UxOXk>z z)UotK;G@!~bl{ec>+*4QAHft;DncqxM4OV6pEZ*wl4D`aDWy_|+aRYK@Bu<{fv{nA zhU+j7fhVVAxXG#Hf^~Xt+%)=D1L{FA&2*jv2LBNvz66oZ|P`*VV~d ztDB+4;xLfCIMStX_DSiTF5i8ni^8=epGprMK7kB)r-k6FH4{>!Ag7m{AC5OACchvH z44KCz!`0?%edY=^6ardvSw>6MH1_clR(Fbdqzvc)Ii{(dyrnO^SnGgrjyfN8a(X%< z{1fD}mjjol>x8L!eF6s3taCKnW1%S;K87_X9MXL`3_D>i=Q2B$oO~iN`6QOX+j!xv7N~C^AqJktMr;V_>iG&_c9;%!YuK*c_v|*5L;n+~f;PCVe%?vAPeyF+QMG9x964;vIA(lPT94m$a0vNfiHPW8G56*QCk zUL{$g1{rt2K&vwGTNg3puPNiN zCG$_!?lGG$vooU?=STR*BIH{&@g2VSQfLG9!Uq40$u>jH?7Vo(9(*&Ge7$|U^Ig8@ zA6wbE96i)EmcEnO`IgB74hF11;cdtHo)nL!c6>C-vu0F74#R~j6=^*J1 z$=}>XwKpid#3{ssl%(*x1i$;giQi8k+KKbR>tw?OC`3?KneBM>~M&jZ)z88$&23WXCGuU}}-!6Pr z{=l_Y37fAP@%~T&gfgvW=jhM@wR?N-%GGO>Gvcq@5jm?1jE?^}arjNguAD#j^hZFs z`}0-i(=Js0e9Tu5bKR7;HxCc@<7?T}(ZXQf&SvGyk9C_D7onGi*ut8HwF~15>lW58 zY*?6Bm|U1z*toE1;lhPyEu6P-{=&aoP(H%{f&O>q0RN`8!e2yFljU5HwF&VC4DW2A2Ldu;Mo-K;k@~-z}ejL(l5tuLVbPS&$^mEZw4`NHYjcS0c9>x<@=iyVam& z2-qI{rNC}L=b~f~&`JcnYb3uKdW64cj(>2q_~{n;^+IL^^mSCuU-=U3ndzWB|4ct$ VN4S8lp6>cH_4j|e`TrFM{s#|kHqQV6 literal 18944 zcmeHP3v?W3b-pvRJNuBd(ynC5jxBrrmc7ziFWZtGCzkcF1(xkdvV)0Td$l`~Hs0Nl zXIG8|iKCDQC5<7bKpP+r!s8H{v$fB*aVXLi;@ufLr%B68vL(o002M9tTBQC_&1#c*KRrvmh_ z@0sPFRCYhJd~`CI4NqC-gcVDN3#h~ zyA_Rw8sBrH!|gd*5w1{LiFN>@OXwv>QHSxF#fPXyv{flL16Y1>eFi1qd}(y_W=7?I zh1%{i2-lNGi1u=0mgqCAi1Ohl#vJ<6iW474@Sz<75u~*6k?5 z`0!e7xzQ3=nCNKK%35*sglw~D@Iw3WDY~{}yP}qnGJ(jl(mnVvtzw4gZkJrpDqJsO z{d_U1N^ZI*N^~hyuL$TXFIK2c0U|%GAF~^3!|Bx^x4K???{y&RT7Q`{pv~z+X4m@Y zDJ;>|1l-8_Z<1DTV)y){nL$z2ozp!qifjnIMW@#><*!sTek}lI9pkL8ZPn?cn2&!! zs-~s|fckZ_9#ut1X=*@S>k7J>-Cb`3N_`~(xR8PF$W!n zTNkRUZ(7BzpxC%zaN%hTV8d;GLYjM5H|Vm_1Z(iuhN>wTYFdLPONRBZX11()>7|#} z6VfAy=t6~1 zs=J}q6>0FApzE(BZ{0bq)*W=WJXsGg1zqc5un`QA;Oiq+FJD$0cEZ}p#TU4^OQESn z4c13cS-()RNf0iQZNbc0HrJ)6*=JVQTWO7^uvqI(Q7^Qf7WE?Q1yNsOb+~wZv2{$; zORT#^-DrKC>n06EXW4%8^s3#GUYn>FT33jAk#$_umsod-da?BdQ7^H6D(XfH2JFyd z^MzNzs%)?Nhxxp!%JHfO=~ea1e0Syj$xB~_P9R<0pK`ES#jmro>oDgE4=nXT;OOQC zR=kdWly&~5FtpIft(vakBz~ZBGwG!)gy>(;4d5`2M4JK2=WVi zRp|yu2ZITcS|@hFRbLsgRI?poEIFsZ{#^(@G1ctgwdlYRk&m%<%Pp|J$J$}{5ggk| zx5d+gy7e<>tR;Zn1`kb{oxqGJ4I=h2Z8rjFguLZ`gzXcTbsUqdH*@i8E^I&A=0*EO z6h(f-2D<*hDP%C$DOjWH6l}}YgiVQvwd(ZeHqH6W7m;5k1;>1s*w(8dV;}*P4lb)M z8bw|x8?7oDMYbp#E%FN&#UT?Mtl$@6;TIwfG$DHG@CtUxlJpq15fCYXC&G5q#ekQzdGq^P`*3B-iYjv|5 zbxVcb1h-|6!VZGbCK8G+_t6GBfEP#Xf^S9!cr{xJtJ!pc)d-uC>){#Jw@cUK5=R+q zd|^FEFIbPc71+}pV=6o_TF9zKmU)_xcy-vcBzP$ktn?QYRNLyJPe4DuVtRI*;+Ykm z?)P9A+Tq|bjXckUmw3%>=r-4R&C5{NB2+b9jz;}CRl02wZo8dFI11GU-O|1DZUdxR z_)2pR1>Lsm;9Ouev0%cjyg_gCO3-kq*EjVrHJ_>neO9I*KP2NlR_Xm{`FRcO(|*0# z(@@cM1~Ap!fq|9pyumTQbCEwC!4R zl~#aC`{seQsyOvDhh$xXVoU9?5O&Wz5E+7q2nfsURcd6H=Hjrd3+vb_0*59JB~>A< zG}~xm&ybg0vLW(-znbhUV8<6W4g?;D2gtg+ZUxL{1F_V|buP0H%9lqZJ5*w=jk>kI z!rt(fD&0Ejbl_-gAbi|ZBilttlQP!2LvAVLBHP{Ekk1ixVX>eU)#Q=sO6vO~km(OE zS7~$_awDc+=uBs4Uu>W_kyl3chE#dhb>XlklW(2f5L+oveyn2l&ai_d9Z{YGg?A#8 zdLlK*q^dlGe;B8Cq)}M#@AZ4?Q4o`OSV5m5_M= zbwy+gZUb;6^;eN^I*jNg%xWRju?xWFYBZ24a46%jqfw4$pE~ z1GN}9#KkYT;L`x8-%^kEvkfKaocyslJ4Rl!L$U8h`QG+KXS6li(c005gvKYI6w2F? zKvte2`VDFhoGV9iRx&e@WsKk8MsXh^#>)L8v>OL~m;kNZxqlE&Ooz~a4vw^PN6H*y zWL#LDa^;=(uJeHoy{L2$P6rB_$ljb+c+Utam%{i!p?vnD75Fd?k7-387Kh>dVK~=d zhb!skHbNJTx_<4^=`m4ewfjBQv>4NLdQhu&>+~VlCv=@EMfsG*?H`FUCm?r-_I8aa zeBG78sOIDHDlMh^DdKv}h2kq&EQ7tW^Wlh|*6vAHy@EJmo&=_0z+|Gt7^=fPA1y`kZV75i6*4k)R4WZoNtw*^YWikA~Y!z@0 z%q|z@FNyId1%6JHSEB4tvec;U3-g+8S$M0^-NAfsxu%en@2x@aP5B;=y-zE#uc9c;K*M^^9AppTW@ll)+FqIYKD?VeqlPX8f#lBQ0- zls!SyL#tLvDu1nP)x6}wjU3awP1&va=n=bj&^@gAX>bjXNe&e>DteMb1>G)sl0yZ3 zSoQ>mO1gBdq&Wf(Rdm1Id(O923(&Pqa?I!1s#VjM?H58p!5lTO;Ls>&@qU;uBk0=L4d4(ts ziZX^$rx}zBaKC!1XE7xOq)l0?t)TnpZQc%)&mn450=|Q;P(J9}1<1YnFg--KxZgo% zsmFT<)zLS6_o2Mj^8hWOgT6=Uv-ETC;~2eJ{Q|9`zx93O+i+8rpL(iyVHeYe7E{D7h; z52z`{r+i3x2Ia#j&(h;qp-;fiQiJ{u3X1kq^l|k*dW!0N|D$wLuUl200=37fQ%L(f z{ToJ~r=v%GV0`2+Cf% ziY8F{Q4R`faRG@7NF4XbDOXa!Zxrww1^h<9|I+n-#L!Qn4AEy$9-}8+PYB2p0`i1_ zd``^$p@9ETz<(&&DPriK=qlL=rm6OV8X!#N4Ys$A3U2Ra8su6X&dO+nk zufroeA|LE&f`n7UGy-p1u}9m6v%PIc+dymQ=8n#;jXS!!x(B*9b$9n~ zZ0qW3Z*OhwZtoiC-rOFJ@K9L`>2(>bFrmW$DZ zkvlS&m2%OrF=bl0zGQ47V`d>J^<-kHnKu|iMm8IpF!ox*Q*kQp6i^2fG%}NkABb5c zu%79hxz94E3@bNNG9dZ#8k6y4E;Z9<#8WZLNH{D9l13^aq+=z$Hko1i=`k}UnsW9| zBV$;}_>hsCG!rzKg>-g1FSi(B?43r=9^)Q1$nIr~Ts9ZWBx082)@wrH$qWvsdtzxL zI~9u?LW-U#mQ-v7)|rc)95%)c%gA6@_V?gxGsw_?(pk|^%sOTyMq=ZPk&Jy zIF^I2u|%8kSef>o+u9S5!R(G$HW_DO^B8~>I)m8*vD9>-Urx)O;1d)rxjp(u<%ky zPd00$$5Jz+u&pA*(U=7-4B)#@;~LXCR-oYE4?vm&hLwfK%^e)fjGIhD9nTdeI$Gep!JZceoEc0x zmYFFG*rq4ox#U?6kU^W5vw1S5?RQJW$?sA5e2iSqp?&Di|5hv|#R)jn0tcV`Aa-o^M)?j85fiIUG zu*@{|#t?*H|C}X8&0|J}b{e_eri@hx=@Ur=ADgF4SNVN1#_f0{xa_r(6G;U8K4TmK zkjYVhhTRHFiBDSycDeqOMtoX~1^Z9fS*G zSy)xvGPCA*4xu?AWy&VA(P9+d%8}_Q&WlE(c)%v-*t!$RMhxpjGHzt;I0f5BUPpIi z5)|yk<{dE0TUFF^Jea+U$B^DQC>9GRhvpX?;Z%kmW8SGe%(*Qqea^NJA;S=YC8C~_ zi*^|)SR(7E)a%5=yu4yN3dactC11s zoSp=uUTJeQWS%hg@Vgk2fG_}T6R(D$al3{pWhO3A69=uL5GMQP)RF$I38{o!kv|kjz&dwcwv69qC9DO@#gTh;DO1^BzWZ@XBPKf8}OF~HOB)oLXs%9l{ae=ni#_zw*C!N>?s?>M#n*8 z17ynx`DW-1m~9Ezyk1h~Yo+@$*MO;|m}3Ad3ANc*_>oZ>Z|ch1&QKIgZzdH5~A=$3XFT!>G`;R+>vnaq zROi*q>SEc>i`DXqG`3B~bSK!!Y{8z%`GmcRV@MqRDfkF`SsF0T2ka-btaLW}eGWcn zVJ3%f<#G<)O_;-Bn1g32#tHbPj0zm9!n7arl6a2JQS2C=9xBzP5W2hbF?U%-}Bqe$Lj=T`LkW-&+IUwYA;qGwZpNOBVl3PGEcIWGt@Fi zw@y@jCE8JX=I2?QG?UO3r@jnw21kC@^%g*6np9>#T^zU>H4QXAILKsv4JL39`tt;kb#vXbfX}B3PUtCY z6m7UL<`ndA!U=e1a&QY;>=?vcSEc^n*6>FO@1`tsIF#-cb7wiq6JDwOgIJS1s<2f- z-WKVc-0p{oa?0j_6UEU~hMB!c-d6(>2R>b1s`V=Y=YuV;egv&?wAdEqp>iI*Z{*a# zLCAoW^8;!V*eUdwIv)`DSi-!E!yX|pUwjs62n@?FgWnv%-wwzSM_Y!Yfx>>4g^pGk zI^v}%Yku#E!PDaT)rE!rSmS*3=A$?tjpq6m|a-hv1O1kdEN59YeI105Ss-( zz7Gh4yNSPJfUJY5a`2UAD^Jsm%X!w}$2-afZvo0i&KgfbswqgwPT)ABlrApj%GJ!( znp_`q<9(I)08X-S_;IS>T`o4%LJGNxF6^?0g?uup$UWt@P@Q@u>M(?TImd}Lg? zKu2}udG_Ut&#})rJe4|H!Kj>#a?D1WR&jJ|m8_gk32aa2x1`cV9u~OHG2B^5dH5>VufwI_VCBNg09n2S z3FUy}EUh%mO8&*ldv#89YGTZBrnR)h|^M!=sfWVQj{22xxsf!PE(&3$hlcPuu z7rfIKj)|GT|66;iNiT5l_v4xNL3nD%F{QiffF<< zfWvipnw>9UyjtmAuK53Fs^m1E7CFhnqprDk2E~EtGPoAZ>1m(U` zdU7lEoVlX&v%BIOkw1h~eEu^nKWCqS8@)P#&<=Uz4G+KjrXMQ<58c=B$lI-Z*ODu& zC|)g0iW`k!kh=lV?AF7o5@-xm)F{e=;1$YJT1qaz5*pMQarREng3#>yl;A-((3ULp zk*b7d9|+Dq6hPk|Y7EYPupM}Zd44FSh75H!GI_y&d zxPr5v$3SDCl2rvEmNG)n;|X1-s-fd@QD`v#8N8s&Q|de(xdyp9sgMefSFi*d6`}`c zp8;PrP+g;FjK({_e;$t(n!Q0)Ydm^r_U0lM%-}ko#}%4=3$tF}@uLz-hx%vG2&kbM zM*%ZZ0W(mY*YELSrQ#BNzp8s&+^Ynw3-0?>T)L zP(HOO1Ik0^JivA;14_@(^hUR;<6l72VdA>5B~1^mVux_EO*Fb4@dt&8>~|CV;2>Tl zgHi){iA(`N4P7V784rUGhh}`F0f}3Fa@nnbm~$8v@MmxKHc3k#^eXnx#h3D1%%f`k zl~!zOkD2K|88@cHm+zC7c}-RU9^S*mwNs%*(LMd6c&$OaU({^B53%h;d$bi40(JQp zQS2{8`MrZ6PYFA7!a&9A%r$eqi6y^6Zt*S*esu$mZFuP(e?%P$MMWpljaR{98{_d! zo4dyoow1Frn_J_t@vivz#*WU;&52EkO-5^5rxC*|?=0^cyz+XCUy>VDt9nu?^O~XQ zR4$2k&;an+ZyezLETHYiPx&r}<`%Ddqr_E#SG#+~8_9TQUA*R9{C3{c=Jo$e%FsR) zm?l20NAT(4dGcZswfOK$#dl-u?xHcq;NJ@rU&AAPBcE=3;={|oxN7I${KePb_JiZM z-On`owj9FSB)J)UnakffXAeo8+EwAzl0)X$)rW*;4;8%}hJBcW8J$Xu(a5fz_Klm! z=J+j#BUW|IjSB)#=bmpoeeL@``pkbra_90MMJw#|N^9rW{t}9}NUXk8Y6#!jQ#uOC|d|JMzA)b+D-(qGw&_>%SIvV-4gl)hQZ z;68j5`+_3O`q6b}{LqJd&!FZ~ zx<}w}Hj#gwIBG)&cnBQCTz)>s&)b;C3|1r`YcuuIhuJ+C=i9BqH_(jp8bQP1*(YwY z;*dBAiq0eJmrD!(3j*YEXdM78eySiJ6)}xAoI6|dALhLP_^h$wC!mEDTulBbZVLHH zC|^y8)ri^PIU!cbTA9LHc_kBgriX|7A{Y$|o_lRL-&?az9GSVtH!~O4T`_OD5AxAT z0w-*K4q8~n#c8qpZ^C_sg75eE!IMLyT!RJuMW`Ep?bHQ%H-cXrzYE(8$T;S93Ro-p zt$_2x`7YGs!0W(RC*TQS^Tz;28zB4ynJ@gm0Fa>vd&W1~d_yOnGM`@#-p}9BbyWVJ TF-v}D1b;_z{fW%)MFal_KlwHe diff --git a/Mirage.Analyzers.Tests/AwakeStartNetworkStateTests.cs b/Mirage.Analyzers.Tests/AwakeStartNetworkStateTests.cs new file mode 100644 index 00000000000..6991fd7e4be --- /dev/null +++ b/Mirage.Analyzers.Tests/AwakeStartNetworkStateTests.cs @@ -0,0 +1,185 @@ +using NUnit.Framework; +using System.Threading.Tasks; + +namespace Mirage.Analyzers.Tests +{ + [TestFixture] + public class AwakeStartNetworkStateTests + { + private const string MockDefinitions = @" +namespace Mirage +{ + public class NetworkBehaviour + { + public bool IsServer { get; } + public bool IsClient { get; } + public bool HasAuthority { get; } + public bool IsLocalPlayer { get; } + public bool IsOwner { get; } + public bool IsHost { get; } + } + public class SyncVarAttribute : System.Attribute {} +} +"; + + [Test] + public async Task Positive_AccessInAllowedMethods() + { + // Verify that accessing network state in methods other than Awake/Start does not trigger warning + var code = @" +using Mirage; + +public class ValidBehaviour : NetworkBehaviour +{ + [SyncVar] + public int Health { get; set; } + + [SyncVar] + public int Points; + + public void Update() + { + if (IsServer) + { + Health = 100; + Points = 10; + } + } + + public override void OnStartServer() + { + if (IsClient) + { + var h = Health; + } + } +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task Positive_NonNetworkBehaviourClass() + { + // Verify that a class not inheriting from NetworkBehaviour can use Awake/Start with fields/properties of similar names + var code = @" +public class NonNetworkClass +{ + public bool IsServer { get; set; } + public int Health { get; set; } + + private void Awake() + { + if (IsServer) + { + Health = 100; + } + } +} +"; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task Negative_AccessIsServerInAwake() + { + // Verify accessing IsServer in Awake triggers MIRAGE1401 warning + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + private void Awake() + { + if ({|#0:IsServer|}) + { + } + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1401") + .WithLocation(0) + .WithArguments("IsServer", "Awake"); + + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task Negative_AccessSyncVarPropertyInStart() + { + // Verify accessing SyncVar property in Start triggers MIRAGE1401 warning + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [SyncVar] + public int Health { get; set; } + + private void Start() + { + {|#0:Health|} = 100; + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1401") + .WithLocation(0) + .WithArguments("Health", "Start"); + + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task Negative_AccessSyncVarFieldInAwake() + { + // Verify accessing SyncVar field in Awake triggers MIRAGE1401 warning + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [SyncVar] + public int points; + + private void Awake() + { + var p = {|#0:points|}; + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1401") + .WithLocation(0) + .WithArguments("points", "Awake"); + + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task Edge_NonSyncVarAccessInAwakeStart() + { + // Verify that non-SyncVar fields and properties can be accessed in Awake/Start + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + public int NonSyncField; + public int NonSyncProp { get; set; } + + private void Awake() + { + NonSyncField = 5; + NonSyncProp = 10; + } +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + } +} diff --git a/Mirage.Analyzers.Tests/DirectMutationTests.cs b/Mirage.Analyzers.Tests/DirectMutationTests.cs new file mode 100644 index 00000000000..5dae802f171 --- /dev/null +++ b/Mirage.Analyzers.Tests/DirectMutationTests.cs @@ -0,0 +1,313 @@ +using NUnit.Framework; +using System.Threading.Tasks; + +namespace Mirage.Analyzers.Tests +{ + [TestFixture] + public class DirectMutationTests + { + private const string MockDefinitions = @" +namespace Mirage +{ + public class NetworkBehaviour {} + public class WeaverSafeClassAttribute : System.Attribute {} +} +namespace Mirage.Collections +{ + public interface ISyncObject {} + public class SyncList : ISyncObject + { + public T this[int index] { get => default; set {} } + } + public class SyncDictionary : ISyncObject + { + public TValue this[TKey key] { get => default; set {} } + } +} +"; + + [Test] + public async Task ModifyingLocalArrayDoesNotReportWarning() + { + // Verify that modifying standard local array elements does not trigger MIRAGE1003 + var code = @" +using Mirage; + +public class MyClass +{ + public int Value; +} + +public class MyBehaviour : NetworkBehaviour +{ + public void Modify() + { + var array = new MyClass[5]; + array[0].Value = 10; + } +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task ModifyingStandardListDoesNotReportWarning() + { + // Verify that modifying standard System.Collections.Generic.List elements does not trigger MIRAGE1003 + var code = @" +using System.Collections.Generic; +using Mirage; + +public class MyClass +{ + public int Value; +} + +public class MyBehaviour : NetworkBehaviour +{ + public void Modify() + { + var list = new List(); + list[0].Value = 10; + } +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task AssigningEntireElementDoesNotReportWarning() + { + // Verify that replacing the entire element in SyncList is allowed and does not trigger MIRAGE1003 + var code = @" +using Mirage; +using Mirage.Collections; + +[WeaverSafeClass] +public struct MyStruct +{ + public int Value; +} + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncList mySyncList = new SyncList(); + + public void Modify() + { + mySyncList[0] = new MyStruct { Value = 10 }; + } +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task ReadingElementMemberDoesNotReportWarning() + { + // Verify that reading a member of an element does not trigger MIRAGE1003 + var code = @" +using Mirage; +using Mirage.Collections; + +public class MyClass +{ + public int Value; +} + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncList mySyncList = new SyncList(); + + public void Modify() + { + int val = mySyncList[0].Value; + } +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task DirectMutationOfSyncListElementReportsWarning() + { + // Verify that directly mutating a field of a SyncList element triggers MIRAGE1003 + var code = @" +using Mirage; +using Mirage.Collections; + +public class MyClass +{ + public int Value; +} + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncList mySyncList = new SyncList(); + + public void Modify() + { + {|#0:mySyncList[0]|}.Value = 10; + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1003").WithLocation(0).WithArguments("mySyncList"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task DirectMutationOfSyncDictionaryElementReportsWarning() + { + // Verify that directly mutating a field of a SyncDictionary element triggers MIRAGE1003 + var code = @" +using Mirage; +using Mirage.Collections; + +public class MyClass +{ + public int Value; +} + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncDictionary mySyncDict = new SyncDictionary(); + + public void Modify() + { + {|#0:mySyncDict[1]|}.Value = 10; + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1003").WithLocation(0).WithArguments("mySyncDict"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task DirectMutationWithCompoundAssignmentReportsWarning() + { + // Verify that compound assignments like += on elements trigger MIRAGE1003 + var code = @" +using Mirage; +using Mirage.Collections; + +public class MyClass +{ + public int Value; +} + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncList mySyncList = new SyncList(); + + public void Modify() + { + {|#0:mySyncList[0]|}.Value += 5; + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1003").WithLocation(0).WithArguments("mySyncList"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task DirectMutationWithUnaryExpressionReportsWarning() + { + // Verify that unary operators like ++ trigger MIRAGE1003 + var code = @" +using Mirage; +using Mirage.Collections; + +public class MyClass +{ + public int Value; +} + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncList mySyncList = new SyncList(); + + public void Modify() + { + {|#0:mySyncList[0]|}.Value++; + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1003").WithLocation(0).WithArguments("mySyncList"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task PassingElementMemberAsRefParamReportsWarning() + { + // Verify that passing an element member by reference triggers MIRAGE1003 + var code = @" +using Mirage; +using Mirage.Collections; + +public class MyClass +{ + public int Value; +} + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncList mySyncList = new SyncList(); + + public void Modify() + { + Helper(ref {|#0:mySyncList[0]|}.Value); + } + + private void Helper(ref int val) + { + val = 5; + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1003").WithLocation(0).WithArguments("mySyncList"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task NestedMemberAccessMutationReportsWarning() + { + // Verify that deeply nested member access mutation also triggers MIRAGE1003 + var code = @" +using Mirage; +using Mirage.Collections; + +public class NestedClass +{ + public int Value; +} + +public class MyClass +{ + public NestedClass Nested = new NestedClass(); +} + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncList mySyncList = new SyncList(); + + public void Modify() + { + {|#0:mySyncList[0]|}.Nested.Value = 10; + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1003").WithLocation(0).WithArguments("mySyncList"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + } +} diff --git a/Mirage.Analyzers.Tests/FieldSerializationTests.cs b/Mirage.Analyzers.Tests/FieldSerializationTests.cs new file mode 100644 index 00000000000..3cc737566de --- /dev/null +++ b/Mirage.Analyzers.Tests/FieldSerializationTests.cs @@ -0,0 +1,311 @@ +using NUnit.Framework; +using System.Threading.Tasks; + +namespace Mirage.Analyzers.Tests +{ + [TestFixture] + public class FieldSerializationTests + { + private const string MockDefinitions = @" +namespace Mirage +{ + public class NetworkMessageAttribute : System.Attribute {} + public class WeaverSafeClassAttribute : System.Attribute {} + public class ServerRpcAttribute : System.Attribute {} + public class ClientRpcAttribute : System.Attribute {} + public class NetworkBehaviour {} +} + +namespace Mirage.Serialization +{ + public class NetworkWriter {} + public class NetworkReader {} +} + +namespace Cysharp.Threading.Tasks +{ + public struct UniTask {} + public struct UniTask {} +} + +namespace UnityEngine +{ + public class GameObject {} + public struct Vector3 {} +} +"; + + [Test] + public async Task PrimitiveAndSupportedTypesDoNotReportError() + { + var code = @" +using Mirage; +using UnityEngine; +using System; + +[NetworkMessage] +public struct ValidMessage +{ + public int myInt; + public string myString; + public Vector3 myVector; + public Guid myGuid; + public DateTime myDateTime; + public byte[] myByteArray; +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task CustomTypeWithSerializerDoesNotReportError() + { + var code = @" +using Mirage; +using Mirage.Serialization; + +public struct CustomType +{ + public int value; +} + +public static class CustomSerialization +{ + public static void WriteCustomType(this NetworkWriter writer, CustomType value) {} + public static CustomType ReadCustomType(this NetworkReader reader) => default; +} + +[NetworkMessage] +public struct ValidMessage +{ + public CustomType customValue; +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task PrivateFieldsAreIgnored() + { + var code = @" +using Mirage; +using System.Threading; + +[NetworkMessage] +public struct MessageWithPrivateField +{ + private Thread executionThread; +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task UnserializableFieldReportsError() + { + var code = @" +using Mirage; +using System.Threading; + +[NetworkMessage] +public struct StartSessionMessage +{ + public Thread {|#0:executionThread|}; +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1302") + .WithLocation(0) + .WithArguments("Thread", "NetworkMessage field"); + + // Note: Since Thread is a class type without WeaverSafeClass attribute, it also triggers MIRAGE1301. + var expectedClassWarning = VerifyCS.Diagnostic("MIRAGE1301") + .WithLocation(0) + .WithArguments("NetworkMessage field", "executionThread", "Thread"); + + await VerifyCS.VerifyAnalyzerAsync(code, expected, expectedClassWarning); + } + + [Test] + public async Task UnserializablePropertyReportsError() + { + var code = @" +using Mirage; +using System.Threading; + +[NetworkMessage] +public struct StartSessionMessage +{ + public Thread {|#0:ExecutionThread|} { get; set; } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1302") + .WithLocation(0) + .WithArguments("Thread", "NetworkMessage property"); + + var expectedClassWarning = VerifyCS.Diagnostic("MIRAGE1301") + .WithLocation(0) + .WithArguments("NetworkMessage property", "ExecutionThread", "Thread"); + + await VerifyCS.VerifyAnalyzerAsync(code, expected, expectedClassWarning); + } + + [Test] + public async Task UnserializableRpcParameterReportsError() + { + var code = @" +using Mirage; +using System.Threading; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc] + public void CmdStartSession(Thread {|#0:executionThread|}) {} +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1302") + .WithLocation(0) + .WithArguments("Thread", "RPC parameter"); + + var expectedClassWarning = VerifyCS.Diagnostic("MIRAGE1301") + .WithLocation(0) + .WithArguments("RPC parameter", "executionThread", "Thread"); + + await VerifyCS.VerifyAnalyzerAsync(code, expected, expectedClassWarning); + } + + [Test] + public async Task UnserializableRpcReturnTypeReportsError() + { + var code = @" +using Mirage; +using System.Threading; +using Cysharp.Threading.Tasks; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc] + public UniTask {|#0:CmdGetSession|}() => default; +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1302") + .WithLocation(0) + .WithArguments("Thread", "RPC return type"); + + var expectedClassWarning = VerifyCS.Diagnostic("MIRAGE1301") + .WithLocation(0) + .WithArguments("RPC return type", "CmdGetSession", "Thread"); + + await VerifyCS.VerifyAnalyzerAsync(code, expected, expectedClassWarning); + } + + [Test] + public async Task StructWithUnserializableFieldReportsError() + { + var code = @" +using Mirage; +using System.Threading; + +public struct NestedUnserializable +{ + public Thread threadField; +} + +[NetworkMessage] +public struct MainMessage +{ + public NestedUnserializable {|#0:nestedField|}; +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1302") + .WithLocation(0) + .WithArguments("NestedUnserializable", "NetworkMessage field"); + + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task RecursiveTypeReportsError() + { + var code = @" +using Mirage; + +[WeaverSafeClass] +public class RecursiveClass +{ + public RecursiveClass {|#0:self|}; +} + +[NetworkMessage] +public struct Message +{ + public RecursiveClass {|#1:recursiveField|}; +} +" + MockDefinitions; + + var expected1 = VerifyCS.Diagnostic("MIRAGE1302") + .WithLocation(0) + .WithArguments("RecursiveClass", "NetworkMessage field"); + + var expected2 = VerifyCS.Diagnostic("MIRAGE1302") + .WithLocation(1) + .WithArguments("RecursiveClass", "NetworkMessage field"); + + await VerifyCS.VerifyAnalyzerAsync(code, expected1, expected2); + } + + [Test] + public async Task MultiDimensionalArrayReportsError() + { + var code = @" +using Mirage; + +[NetworkMessage] +public struct MessageWithMultiArray +{ + public int[,] {|#0:multiArray|}; +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1302") + .WithLocation(0) + .WithArguments("Int32", "NetworkMessage field"); + + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task WeaverSafeClassWithUnserializableFieldReportsError() + { + var code = @" +using Mirage; +using System.Threading; + +[WeaverSafeClass] +public class SafeClassWithThread +{ + public Thread threadField; +} + +[NetworkMessage] +public struct Message +{ + public SafeClassWithThread {|#0:safeClassField|}; +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1302") + .WithLocation(0) + .WithArguments("SafeClassWithThread", "NetworkMessage field"); + + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + } +} diff --git a/Mirage.Analyzers.Tests/MismatchedSerializerTests.cs b/Mirage.Analyzers.Tests/MismatchedSerializerTests.cs new file mode 100644 index 00000000000..0e253bfff84 --- /dev/null +++ b/Mirage.Analyzers.Tests/MismatchedSerializerTests.cs @@ -0,0 +1,153 @@ +using NUnit.Framework; +using System.Threading.Tasks; + +namespace Mirage.Analyzers.Tests +{ + [TestFixture] + public class MismatchedSerializerTests + { + private const string MockDefinitions = @" +namespace Mirage.Serialization +{ + public class NetworkWriter {} + public class NetworkReader {} +} +"; + + [Test] + public async Task MatchingReaderAndWriterDoesNotReportError() + { + var code = @" +using Mirage.Serialization; + +public struct CustomType +{ + public int value; +} + +public static class CustomSerialization +{ + public static void WriteCustomType(this NetworkWriter writer, CustomType value) {} + public static CustomType ReadCustomType(this NetworkReader reader) => default; +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task NonExtensionMethodsAreIgnored() + { + // Non-extension methods should not be treated as custom serializers, + // so they should not trigger any mismatched serialization warnings. + var code = @" +using Mirage.Serialization; + +public struct CustomType {} + +public static class CustomSerialization +{ + // Not extension methods (missing 'this') + public static void WriteCustomType(NetworkWriter writer, CustomType value) {} + public static CustomType ReadCustomType(NetworkReader reader) => default; +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task WriterOnlyReportsError() + { + var code = @" +using Mirage.Serialization; + +public struct CustomType +{ + public int value; +} + +public static class CustomSerialization +{ + public static void {|#0:WriteCustomType|}(this NetworkWriter writer, CustomType value) {} +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1303") + .WithLocation(0) + .WithArguments("CustomType", "Custom writer defined for 'CustomType' but matching custom reader is missing."); + + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task ReaderOnlyReportsError() + { + var code = @" +using Mirage.Serialization; + +public struct CustomType +{ + public int value; +} + +public static class CustomSerialization +{ + public static CustomType {|#0:ReadCustomType|}(this NetworkReader reader) => default; +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1303") + .WithLocation(0) + .WithArguments("CustomType", "Custom reader defined for 'CustomType' but matching custom writer is missing."); + + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task NestedStaticClassWithMismatchedSerializerReportsError() + { + var code = @" +using Mirage.Serialization; + +public struct CustomType {} + +public static class OuterClass +{ + public static class InnerClass + { + public static void {|#0:WriteCustomType|}(this NetworkWriter writer, CustomType value) {} + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1303") + .WithLocation(0) + .WithArguments("CustomType", "Custom writer defined for 'CustomType' but matching custom reader is missing."); + + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task MismatchedArraySerializerReportsError() + { + // Custom serialization for arrays (e.g. CustomType[]) must also match + var code = @" +using Mirage.Serialization; + +public struct CustomType {} + +public static class CustomSerialization +{ + public static void {|#0:WriteCustomArray|}(this NetworkWriter writer, CustomType[] value) {} +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1303") + .WithLocation(0) + .WithArguments("CustomType[]", "Custom writer defined for 'CustomType[]' but matching custom reader is missing."); + + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + } +} diff --git a/Mirage.Analyzers.Tests/MtuSizeEstimationTests.cs b/Mirage.Analyzers.Tests/MtuSizeEstimationTests.cs new file mode 100644 index 00000000000..93c0789cadd --- /dev/null +++ b/Mirage.Analyzers.Tests/MtuSizeEstimationTests.cs @@ -0,0 +1,135 @@ +using NUnit.Framework; +using System.Threading.Tasks; + +namespace Mirage.Analyzers.Tests +{ + [TestFixture] + public class MtuSizeEstimationTests + { + private const string MockDefinitions = @" +namespace Mirage +{ + public class NetworkMessageAttribute : System.Attribute {} + public class NetworkBehaviour {} + public class NetworkIdentity {} +} +namespace Mirage.Serialization +{ + public class BitCountAttribute : System.Attribute + { + public BitCountAttribute(int bits) {} + } + public class FloatPackAttribute : System.Attribute + { + public FloatPackAttribute(float min, float max, float precision) {} + public FloatPackAttribute(int bits) {} // overload for testing + public FloatPackAttribute(float max, int bits) {} // overload matching signature in analyzer + } +} +namespace UnityEngine +{ + public struct Vector3 + { + public float x; + public float y; + public float z; + } + public struct Quaternion + { + public float x; + public float y; + public float z; + public float w; + } +} +"; + + [Test] + public async Task Positive_SmallMessageDoesNotTriggerWarning() + { + // Verify that a small network message does not trigger MTU warning (estimated size <= 1200) + var code = @" +using Mirage; +using UnityEngine; + +[NetworkMessage] +public struct SmallMessage +{ + public int id; + public Vector3 position; + public string name; +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task Positive_LargeMessageReducedByPacking() + { + // Verify that bitcount / floatpack attribute overrides size estimation, keeping it within safe MTU + var code = @" +using Mirage; +using Mirage.Serialization; + +[NetworkMessage] +public struct PackedTestMessage +{ + [FloatPack(0.0f, 1)] // Should estimate size based on 1 bit instead of 4/8 bytes + public float CompressedVal; +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task Negative_LargeMessageExceedsMtu() + { + // Verify that a network message exceeding 1200 bytes triggers MIRAGE1501 warning. + // 10 arrays of double: 10 * 128 * 8 = 10240 bytes. + var code = @" +using Mirage; + +[NetworkMessage] +public struct {|#0:HugeMessage|} +{ + public double[] arr1; + public double[] arr2; + public double[] arr3; + public double[] arr4; + public double[] arr5; + public double[] arr6; + public double[] arr7; + public double[] arr8; + public double[] arr9; + public double[] arr10; +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1501") + .WithLocation(0) + .WithArguments("HugeMessage", "10240", "1200"); + + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task Edge_RecursiveStructOrClassRef() + { + // Verify that recursive types do not cause infinite recursion and resolve to 0 or valid size + var code = @" +using Mirage; + +[NetworkMessage] +public class RecursiveMessage +{ + public RecursiveMessage Parent; + public int Value; +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + } +} diff --git a/Mirage.Analyzers.Tests/OnSerializeBaseCallTests.cs b/Mirage.Analyzers.Tests/OnSerializeBaseCallTests.cs new file mode 100644 index 00000000000..53145f4deef --- /dev/null +++ b/Mirage.Analyzers.Tests/OnSerializeBaseCallTests.cs @@ -0,0 +1,199 @@ +using NUnit.Framework; +using System.Threading.Tasks; + +namespace Mirage.Analyzers.Tests +{ + [TestFixture] + public class OnSerializeBaseCallTests + { + private const string MockDefinitions = @" +namespace Mirage +{ + public class NetworkBehaviour + { + public virtual bool OnSerialize(Mirage.Serialization.NetworkWriter writer, bool initialState) => true; + public virtual void OnDeserialize(Mirage.Serialization.NetworkReader reader, bool initialState) {} + } + public class SyncVarAttribute : System.Attribute {} +} +namespace Mirage.Serialization +{ + public class NetworkWriter + { + public void WritePackedInt32(int value) {} + } + public class NetworkReader {} +} +namespace Mirage.Collections +{ + public interface ISyncObject {} + public class SyncList : ISyncObject {} +} +"; + + [Test] + public async Task Positive_BaseCallIncluded() + { + // Verify that calling base.OnSerialize and base.OnDeserialize when base has sync state compiles and triggers no warning + var code = @" +using Mirage; +using Mirage.Serialization; + +public class BasePlayer : NetworkBehaviour +{ + [SyncVar] + public string PlayerName { get; set; } +} + +public class HeroPlayer : BasePlayer +{ + [SyncVar] + public int HeroId { get; set; } + + public override bool OnSerialize(NetworkWriter writer, bool initialState) + { + bool baseDirty = base.OnSerialize(writer, initialState); + writer.WritePackedInt32(HeroId); + return baseDirty || true; + } + + public override void OnDeserialize(NetworkReader reader, bool initialState) + { + base.OnDeserialize(reader, initialState); + } +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task Positive_NoBaseSyncState() + { + // Verify that overriding OnSerialize/OnDeserialize without calling base does not trigger warning if base has no sync state + var code = @" +using Mirage; +using Mirage.Serialization; + +public class EmptyBase : NetworkBehaviour +{ + // No SyncVars or ISyncObjects here +} + +public class DerivedPlayer : EmptyBase +{ + [SyncVar] + public int HeroId { get; set; } + + public override bool OnSerialize(NetworkWriter writer, bool initialState) + { + writer.WritePackedInt32(HeroId); + return true; + } + + public override void OnDeserialize(NetworkReader reader, bool initialState) + { + } +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task Negative_MissingBaseOnSerialize() + { + // Verify that missing base.OnSerialize when base has sync state triggers MIRAGE1402 warning + var code = @" +using Mirage; +using Mirage.Serialization; + +public class BasePlayer : NetworkBehaviour +{ + [SyncVar] + public string PlayerName { get; set; } +} + +public class HeroPlayer : BasePlayer +{ + [SyncVar] + public int HeroId { get; set; } + + public override bool {|#0:OnSerialize|}(NetworkWriter writer, bool initialState) + { + writer.WritePackedInt32(HeroId); + return true; + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1402") + .WithLocation(0) + .WithArguments("OnSerialize"); + + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task Negative_MissingBaseOnDeserialize() + { + // Verify that missing base.OnDeserialize when base has sync state triggers MIRAGE1402 warning + var code = @" +using Mirage; +using Mirage.Serialization; + +public class BasePlayer : NetworkBehaviour +{ + [SyncVar] + public string PlayerName { get; set; } +} + +public class HeroPlayer : BasePlayer +{ + [SyncVar] + public int HeroId { get; set; } + + public override void {|#0:OnDeserialize|}(NetworkReader reader, bool initialState) + { + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1402") + .WithLocation(0) + .WithArguments("OnDeserialize"); + + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task Edge_BaseSyncStateFromISyncObject() + { + // Verify warning is triggered if base class only has ISyncObject (like SyncList) and base call is missing + var code = @" +using Mirage; +using Mirage.Serialization; +using Mirage.Collections; + +public class BasePlayer : NetworkBehaviour +{ + public SyncList Scores = new SyncList(); +} + +public class HeroPlayer : BasePlayer +{ + public override bool {|#0:OnSerialize|}(NetworkWriter writer, bool initialState) + { + return true; + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1402") + .WithLocation(0) + .WithArguments("OnSerialize"); + + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + } +} diff --git a/Mirage.Analyzers.Tests/RateLimitSettingsTests.cs b/Mirage.Analyzers.Tests/RateLimitSettingsTests.cs new file mode 100644 index 00000000000..938d6bb6faa --- /dev/null +++ b/Mirage.Analyzers.Tests/RateLimitSettingsTests.cs @@ -0,0 +1,192 @@ +using NUnit.Framework; +using System.Threading.Tasks; + +namespace Mirage.Analyzers.Tests +{ + [TestFixture] + public class RateLimitSettingsTests + { + private const string MockDefinitions = @" +namespace Mirage +{ + public class NetworkBehaviour {} + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ServerRpcAttribute : System.Attribute {} + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ClientRpcAttribute : System.Attribute {} + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class RateLimitAttribute : System.Attribute + { + public float Interval { get; set; } + public int Refill { get; set; } + public int MaxTokens { get; set; } + } +} +"; + + [Test] + public async Task ValidRateLimitSettings() + { + // Verify rate limit settings with positive values and MaxTokens >= Refill compile and analyze cleanly + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc] + [RateLimit(Interval = 0.5f, Refill = 5, MaxTokens = 10)] + public void CmdFire() + { + } +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task ValidRateLimitDefaultSettings() + { + // Verify default rate limit settings are automatically treated as valid + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc] + [RateLimit] + public void CmdFire() + { + } +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task InvalidRateLimitInterval() + { + // Verify an interval of zero or lower triggers the appropriate validation warning + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc] + [RateLimit(Interval = 0f)] + public void {|#0:CmdFire|}() + { + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1205") + .WithLocation(0) + .WithArguments("CmdFire", "Interval must be greater than zero"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task InvalidRateLimitRefillAndMaxTokens() + { + // Verify non-positive refills or tokens trigger warnings + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc] + [RateLimit(Refill = -5, MaxTokens = 0)] + public void {|#0:CmdFire|}() + { + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1205") + .WithLocation(0) + .WithArguments("CmdFire", "Refill must be greater than zero, MaxTokens must be greater than zero"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task InvalidRateLimitMaxTokensLessThanRefill() + { + // Verify that maximum allowed tokens cannot be configured below the refill amount + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc] + [RateLimit(Refill = 10, MaxTokens = 5)] + public void {|#0:CmdFire|}() + { + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1205") + .WithLocation(0) + .WithArguments("CmdFire", "MaxTokens must be greater than or equal to Refill"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task RateLimitOnNonRpcMethodIgnored() + { + // Verify that rate limit constraints are only enforced on network RPC methods + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [RateLimit(Interval = 0f, Refill = 0, MaxTokens = 0)] + public void LocalFire() + { + } +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task CustomRateLimitAttributeIgnored() + { + // Verify external rate limiting attributes do not trigger Mirage-specific validations + var code = @" +using Mirage; + +namespace CustomNamespace +{ + public class RateLimitAttribute : System.Attribute + { + public float Interval { get; set; } + public int Refill { get; set; } + public int MaxTokens { get; set; } + } +} + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc] + [CustomNamespace.RateLimit(Interval = -1f)] + public void CmdFire() + { + } +} +" + MockDefinitions; + + // Note: Since Mirage's [RateLimit] is missing on ServerRpc here, it will trigger MIRAGE1206 + var expected = VerifyCS.Diagnostic("MIRAGE1206") + .WithLocation(0) + .WithArguments("CmdFire"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + } +} diff --git a/Mirage.Analyzers.Tests/RpcClientTargetTests.cs b/Mirage.Analyzers.Tests/RpcClientTargetTests.cs new file mode 100644 index 00000000000..da3308286af --- /dev/null +++ b/Mirage.Analyzers.Tests/RpcClientTargetTests.cs @@ -0,0 +1,213 @@ +using NUnit.Framework; +using System.Threading.Tasks; + +namespace Mirage.Analyzers.Tests +{ + [TestFixture] + public class RpcClientTargetTests + { + private const string MockDefinitions = @" +namespace Mirage +{ + public class NetworkBehaviour {} + + public enum RpcTarget + { + Owner = 0, + Observers = 1, + Player = 2 + } + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ClientRpcAttribute : System.Attribute + { + public RpcTarget target { get; set; } + } + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ServerRpcAttribute : System.Attribute {} + + public interface INetworkPlayer {} + public class NetworkPlayer : INetworkPlayer {} + public class NetworkConnection {} +} + +namespace Cysharp.Threading.Tasks +{ + public struct UniTask {} + public struct UniTask {} +} +"; + + [Test] + public async Task ValidClientRpcObserversReturnsVoid() + { + // Verify client RPCs default to Observers and allow void returns safely + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ClientRpc] + public void RpcUpdate(int value) + { + } +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task ValidClientRpcPlayerWithNetworkPlayer() + { + // Verify targeted client RPCs allow INetworkPlayer as target parameters + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ClientRpc(target = RpcTarget.Player)] + public void RpcMessage(INetworkPlayer player, string msg) + { + } +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task ValidClientRpcPlayerWithNetworkConnection() + { + // Verify targeted client RPCs allow NetworkConnection as target parameters + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ClientRpc(target = RpcTarget.Player)] + public void RpcMessage(NetworkConnection conn, string msg) + { + } +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task ValidClientRpcOwnerWithUniTask() + { + // Verify targeted client RPCs to the owner can return UniTask values + var code = @" +using Mirage; +using Cysharp.Threading.Tasks; + +public class MyBehaviour : NetworkBehaviour +{ + [ClientRpc(target = RpcTarget.Owner)] + public UniTask RpcCalculate() + { + return default; + } +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task InvalidClientRpcObserversWithUniTask() + { + // Verify broadcast RPCs cannot return task values because of multi-client response ambiguity + var code = @" +using Mirage; +using Cysharp.Threading.Tasks; + +public class MyBehaviour : NetworkBehaviour +{ + [ClientRpc(target = RpcTarget.Observers)] + public UniTask {|#0:RpcCalculate|}() + { + return default; + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1204") + .WithLocation(0) + .WithArguments("RpcCalculate", "must return void when target is Observers"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task InvalidClientRpcPlayerWithoutConnectionParameter() + { + // Verify targeted client RPCs require a player connection identifier as the first parameter + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ClientRpc(target = RpcTarget.Player)] + public void {|#0:RpcMessage|}(string msg) + { + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1204") + .WithLocation(0) + .WithArguments("RpcMessage", "method with target = Player requires first parameter to be INetworkPlayer or NetworkConnection"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task InvalidClientRpcPlayerWithWrongFirstParameter() + { + // Verify targeted client RPCs reject non-connection types for the first parameter + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ClientRpc(target = RpcTarget.Player)] + public void {|#0:RpcMessage|}(int connectionId, string msg) + { + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1204") + .WithLocation(0) + .WithArguments("RpcMessage", "method with target = Player requires first parameter to be INetworkPlayer or NetworkConnection"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task CustomClientRpcAttributeIgnored() + { + // Verify the analyzer does not run checks on client RPC attributes defined outside of Mirage + var code = @" +namespace CustomNamespace +{ + public class ClientRpcAttribute : System.Attribute + { + public int target { get; set; } + } +} + +public class MyBehaviour +{ + [CustomNamespace.ClientRpc(target = 2)] + public void RpcMessage(int connectionId) + { + } +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + } +} diff --git a/Mirage.Analyzers.Tests/RpcPassByRefTests.cs b/Mirage.Analyzers.Tests/RpcPassByRefTests.cs new file mode 100644 index 00000000000..ccb28a3a922 --- /dev/null +++ b/Mirage.Analyzers.Tests/RpcPassByRefTests.cs @@ -0,0 +1,177 @@ +using NUnit.Framework; +using System.Threading.Tasks; + +namespace Mirage.Analyzers.Tests +{ + [TestFixture] + public class RpcPassByRefTests + { + private const string MockDefinitions = @" +namespace Mirage +{ + public class NetworkBehaviour {} + public class ServerRpcAttribute : System.Attribute {} + public class ClientRpcAttribute : System.Attribute {} + public class RateLimitAttribute : System.Attribute {} +} +"; + + [Test] + public async Task RpcWithValueAndRefParametersDoesNotReportWarning() + { + // Verify that normal RPCs with value parameters or in parameters do not trigger MIRAGE1202 + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc, RateLimit] + public void CmdDoSomething(int value, string message) {} + + [ClientRpc] + public void RpcDoSomething(int value, string message) {} +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task RpcWithInParameterDoesNotReportWarning() + { + // Verify that 'in' parameters (which are read-only references) do not trigger MIRAGE1202 + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc, RateLimit] + public void CmdDoSomething(in int value) {} +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task NonRpcMethodWithRefOrOutDoesNotReportWarning() + { + // Verify that non-RPC methods can use ref/out parameters freely + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + public void LocalHelper(ref int value, out string result) + { + value += 1; + result = ""done""; + } +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task FakeRpcWithRefParameterDoesNotReportWarning() + { + // Verify that a custom attribute with the same name in a different namespace does not trigger MIRAGE1202 + var code = @" +using System; +using Mirage; + +namespace Custom +{ + public class ServerRpcAttribute : Attribute {} +} + +public class MyBehaviour : NetworkBehaviour +{ + [Custom.ServerRpc] + public void CmdFakeRpc(ref int value) {} +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task ServerRpcWithRefParameterReportsError() + { + // Verify that a ServerRpc with a ref parameter triggers MIRAGE1202 + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc, RateLimit] + public void CmdDoSomething(ref int {|#0:value|}) {} +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1202").WithLocation(0).WithArguments("CmdDoSomething", "value"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task ServerRpcWithOutParameterReportsError() + { + // Verify that a ServerRpc with an out parameter triggers MIRAGE1202 + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc, RateLimit] + public void CmdDoSomething(out int {|#0:value|}) + { + value = 0; + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1202").WithLocation(0).WithArguments("CmdDoSomething", "value"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task ClientRpcWithRefParameterReportsError() + { + // Verify that a ClientRpc with a ref parameter triggers MIRAGE1202 + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ClientRpc] + public void RpcDoSomething(ref int {|#0:value|}) {} +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1202").WithLocation(0).WithArguments("RpcDoSomething", "value"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task ClientRpcWithOutParameterReportsError() + { + // Verify that a ClientRpc with an out parameter triggers MIRAGE1202 + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ClientRpc] + public void RpcDoSomething(out int {|#0:value|}) + { + value = 0; + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1202").WithLocation(0).WithArguments("RpcDoSomething", "value"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + } +} diff --git a/Mirage.Analyzers.Tests/RpcStaticTests.cs b/Mirage.Analyzers.Tests/RpcStaticTests.cs new file mode 100644 index 00000000000..86109b9735f --- /dev/null +++ b/Mirage.Analyzers.Tests/RpcStaticTests.cs @@ -0,0 +1,114 @@ +using NUnit.Framework; +using System.Threading.Tasks; + +namespace Mirage.Analyzers.Tests +{ + [TestFixture] + public class RpcStaticTests + { + private const string MockDefinitions = @" +namespace Mirage +{ + public class NetworkBehaviour {} + public class ServerRpcAttribute : System.Attribute {} + public class ClientRpcAttribute : System.Attribute {} + public class RateLimitAttribute : System.Attribute {} +} +"; + + [Test] + public async Task InstanceRpcDoesNotReportWarning() + { + // Verify that instance-level RPC methods (both ServerRpc and ClientRpc) do not trigger MIRAGE1203 + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc, RateLimit] + public void CmdDoSomething() {} + + [ClientRpc] + public void RpcDoSomething() {} +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task StaticNonRpcMethodDoesNotReportWarning() + { + // Verify that static methods that are not RPCs do not trigger MIRAGE1203 + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + public static void LocalHelper() {} +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task FakeStaticRpcDoesNotReportWarning() + { + // Verify that a fake ServerRpc attribute in a different namespace does not trigger MIRAGE1203 on static methods + var code = @" +using System; +using Mirage; + +namespace Custom +{ + public class ServerRpcAttribute : Attribute {} +} + +public class MyBehaviour : NetworkBehaviour +{ + [Custom.ServerRpc] + public static void CmdFakeRpc() {} +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task StaticServerRpcReportsError() + { + // Verify that a static ServerRpc method triggers MIRAGE1203 + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc, RateLimit] + public static void {|#0:CmdDoSomething|}() {} +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1203").WithLocation(0).WithArguments("CmdDoSomething"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task StaticClientRpcReportsError() + { + // Verify that a static ClientRpc method triggers MIRAGE1203 + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ClientRpc] + public static void {|#0:RpcDoSomething|}() {} +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1203").WithLocation(0).WithArguments("RpcDoSomething"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + } +} diff --git a/Mirage.Analyzers.Tests/ServerRpcRateLimitMissingTests.cs b/Mirage.Analyzers.Tests/ServerRpcRateLimitMissingTests.cs new file mode 100644 index 00000000000..05471181538 --- /dev/null +++ b/Mirage.Analyzers.Tests/ServerRpcRateLimitMissingTests.cs @@ -0,0 +1,119 @@ +using NUnit.Framework; +using System.Threading.Tasks; + +namespace Mirage.Analyzers.Tests +{ + [TestFixture] + public class ServerRpcRateLimitMissingTests + { + private const string MockDefinitions = @" +namespace Mirage +{ + public class NetworkBehaviour {} + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ServerRpcAttribute : System.Attribute {} + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ClientRpcAttribute : System.Attribute {} + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class RateLimitAttribute : System.Attribute + { + public float Interval { get; set; } + public int Refill { get; set; } + public int MaxTokens { get; set; } + } +} +"; + + [Test] + public async Task ServerRpcWithRateLimitDoesNotReportWarning() + { + // Verify that server RPCs with a rate limit are considered secure and do not warn + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc] + [RateLimit(Interval = 1f, Refill = 10, MaxTokens = 10)] + public void CmdInteract() + { + } +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task ClientRpcWithoutRateLimitDoesNotReportWarning() + { + // Verify client-bound RPCs do not require rate limits since they run on trusted clients + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ClientRpc] + public void RpcUpdateInteract() + { + } +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task ServerRpcWithoutRateLimitReportsWarning() + { + // Verify that server RPCs without rate limits warn to prevent potential spam attacks + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc] + public void {|#0:CmdInteract|}() + { + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1206") + .WithLocation(0) + .WithArguments("CmdInteract"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task ServerRpcWithCustomRateLimitReportsWarning() + { + // Verify custom external rate limiting attributes are not recognized by Mirage's defense rule + var code = @" +using Mirage; + +namespace CustomNamespace +{ + public class RateLimitAttribute : System.Attribute {} +} + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc] + [CustomNamespace.RateLimit] + public void {|#0:CmdInteract|}() + { + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1206") + .WithLocation(0) + .WithArguments("CmdInteract"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + } +} diff --git a/Mirage.Analyzers.Tests/SyncObjectReassignmentTests.cs b/Mirage.Analyzers.Tests/SyncObjectReassignmentTests.cs new file mode 100644 index 00000000000..e178de39b00 --- /dev/null +++ b/Mirage.Analyzers.Tests/SyncObjectReassignmentTests.cs @@ -0,0 +1,179 @@ +using NUnit.Framework; +using System.Threading.Tasks; + +namespace Mirage.Analyzers.Tests +{ + [TestFixture] + public class SyncObjectReassignmentTests + { + private const string MockDefinitions = @" +namespace Mirage +{ + public class NetworkBehaviour {} +} +namespace Mirage.Collections +{ + public interface ISyncObject {} + public class SyncList : ISyncObject + { + public T this[int index] { get => default; set {} } + } +} +"; + + [Test] + public async Task ReadonlySyncObjectDoesNotReportWarning() + { + // Verify that marking an ISyncObject field as readonly does not trigger MIRAGE1004 + var code = @" +using Mirage; +using Mirage.Collections; + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncList mySyncList = new SyncList(); +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task NonSyncObjectFieldNotReadonlyDoesNotReportWarning() + { + // Verify that a regular field not implementing ISyncObject is allowed to not be readonly + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + public int myNormalField = 0; + + public void Modify() + { + myNormalField = 10; + } +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task SyncObjectAssignedInConstructorDoesNotReportWarning() + { + // Verify that assigning/initializing ISyncObject in constructor does not trigger MIRAGE1004 + var code = @" +using Mirage; +using Mirage.Collections; + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncList mySyncList; + + public MyBehaviour() + { + mySyncList = new SyncList(); + } +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task NonReadonlySyncObjectReportsError() + { + // Verify that an ISyncObject field not marked readonly triggers MIRAGE1004 on field declaration + var code = @" +using Mirage; +using Mirage.Collections; + +public class MyBehaviour : NetworkBehaviour +{ + public SyncList {|#0:mySyncList|}; +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1004").WithLocation(0).WithArguments("mySyncList"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task SyncObjectReassignmentInMethodReportsError() + { + // Verify that reassigning an ISyncObject field outside constructor triggers MIRAGE1004 + var code = @" +using Mirage; +using Mirage.Collections; + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncList mySyncList = new SyncList(); + + public void ResetList() + { + {|#0:mySyncList|} = new SyncList(); + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1004").WithLocation(0).WithArguments("mySyncList"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task SyncObjectReassignmentInLocalFunctionInConstructorReportsError() + { + // Verify that reassigning an ISyncObject inside a local function inside constructor triggers MIRAGE1004 + var code = @" +using Mirage; +using Mirage.Collections; + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncList mySyncList = new SyncList(); + + public MyBehaviour() + { + void LocalMethod() + { + {|#0:mySyncList|} = new SyncList(); + } + LocalMethod(); + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1004").WithLocation(0).WithArguments("mySyncList"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task SyncObjectReassignmentInLambdaInConstructorReportsError() + { + // Verify that reassigning an ISyncObject inside a lambda expression inside constructor triggers MIRAGE1004 + var code = @" +using System; +using Mirage; +using Mirage.Collections; + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncList mySyncList = new SyncList(); + + public MyBehaviour() + { + Action act = () => { + {|#0:mySyncList|} = new SyncList(); + }; + act(); + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1004").WithLocation(0).WithArguments("mySyncList"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + } +} diff --git a/Mirage.Analyzers.Tests/SyncVarClassTests.cs b/Mirage.Analyzers.Tests/SyncVarClassTests.cs new file mode 100644 index 00000000000..8a0062182f1 --- /dev/null +++ b/Mirage.Analyzers.Tests/SyncVarClassTests.cs @@ -0,0 +1,131 @@ +using NUnit.Framework; +using System.Threading.Tasks; + +namespace Mirage.Analyzers.Tests +{ + [TestFixture] + public class SyncVarClassTests + { + private const string MockDefinitions = @" +namespace Mirage +{ + public class NetworkBehaviour {} + public class SyncVarAttribute : System.Attribute {} + public class WeaverSafeClassAttribute : System.Attribute {} +} +namespace Mirage.Collections +{ + public interface ISyncObject {} + public class SyncList : ISyncObject + { + public T this[int index] { get => default; set {} } + } +} +"; + + [Test] + public async Task SafeTypeDoesNotReportWarning() + { + // Verify that primitive type syncvars do not trigger MIRAGE1001 warning + var code = @" +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [SyncVar] + public int mySyncVar; +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task ClassTypeReportsWarning() + { + // Verify that class type syncvars trigger MIRAGE1001 warning with the correct argument description + var code = @" +using Mirage; + +public class MyClass {} + +public class MyBehaviour : NetworkBehaviour +{ + [SyncVar] + public MyClass {|#0:mySyncVar|}; +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1001").WithLocation(0).WithArguments("mySyncVar", "MyClass"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task SyncObjectNotReadonlyReportsError() + { + var code = @" +using Mirage; +using Mirage.Collections; + +public class MyBehaviour : NetworkBehaviour +{ + public SyncList {|#0:mySyncList|}; +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1004").WithLocation(0).WithArguments("mySyncList"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task SyncObjectReassignmentReportsError() + { + var code = @" +using Mirage; +using Mirage.Collections; + +public class MyBehaviour : NetworkBehaviour +{ + public SyncList {|#0:mySyncList|} = new SyncList(); + + public void Modify() + { + {|#1:mySyncList|} = new SyncList(); + } +} +" + MockDefinitions; + + var expectedField = VerifyCS.Diagnostic("MIRAGE1004").WithLocation(0).WithArguments("mySyncList"); + var expectedAssignment = VerifyCS.Diagnostic("MIRAGE1004").WithLocation(1).WithArguments("mySyncList"); + await VerifyCS.VerifyAnalyzerAsync(code, expectedField, expectedAssignment); + } + + [Test] + public async Task DirectMutationOfSyncListElementReportsWarning() + { + var code = @" +using Mirage; +using Mirage.Collections; + +[WeaverSafeClass] +public class MyClass +{ + public int Value; +} + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncList mySyncList = new SyncList(); + + public void Modify() + { + {|#0:mySyncList[0]|}.Value = 10; + } +} +" + MockDefinitions; + + var expected = VerifyCS.Diagnostic("MIRAGE1003").WithLocation(0).WithArguments("mySyncList"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + } +} diff --git a/Mirage.Analyzers.Tests/UnboundedCollectionTests.cs b/Mirage.Analyzers.Tests/UnboundedCollectionTests.cs new file mode 100644 index 00000000000..3f5cb664d1a --- /dev/null +++ b/Mirage.Analyzers.Tests/UnboundedCollectionTests.cs @@ -0,0 +1,124 @@ +using NUnit.Framework; +using System.Threading.Tasks; + +namespace Mirage.Analyzers.Tests +{ + [TestFixture] + public class UnboundedCollectionTests + { + private const string MockDefinitions = @" +namespace Mirage +{ + public class NetworkMessageAttribute : System.Attribute {} + public class NetworkBehaviour {} + public class ServerRpcAttribute : System.Attribute {} + public class ClientRpcAttribute : System.Attribute {} +} +namespace Mirage.Serialization +{ + public class BitCountAttribute : System.Attribute + { + public BitCountAttribute(int bits) {} + } + public class VarIntAttribute : System.Attribute {} + public class BitCountFromRangeAttribute : System.Attribute {} + public class VarIntBlocksAttribute : System.Attribute {} +} +"; + + [Test] + public async Task Positive_BoundedStringAndCollection() + { + // Verify that strings and collections with a bit packing attribute do not trigger a warning + var code = @" +using Mirage; +using Mirage.Serialization; +using System.Collections.Generic; + +[NetworkMessage] +public struct ValidMessage +{ + [BitCount(8)] + public string Name; + + [VarInt] + public int[] Scores; + + [BitCount(10)] + public List Positions; +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task Positive_NonNetworkContext() + { + // Verify that unbounded strings/collections outside of NetworkMessages or RPCs do not trigger a warning + var code = @" +using System.Collections.Generic; + +public class StandardClass +{ + public string UnboundedString; + public List UnboundedList; + + public void NormalMethod(string param) {} +} +"; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task Negative_UnboundedFieldAndPropertyInMessage() + { + // Verify that unbounded string/collection fields and properties in a NetworkMessage trigger warning + var code = @" +using Mirage; +using System.Collections.Generic; + +[NetworkMessage] +public struct InvalidMessage +{ + public string {|#0:Name|}; + + public int[] {|#1:Scores|}; + + public List {|#2:Positions|} { get; set; } +} +" + MockDefinitions; + + var expectedField1 = VerifyCS.Diagnostic("MIRAGE1502").WithLocation(0).WithArguments("Name", "String"); + var expectedField2 = VerifyCS.Diagnostic("MIRAGE1502").WithLocation(1).WithArguments("Scores", "Int32[]"); + var expectedProp = VerifyCS.Diagnostic("MIRAGE1502").WithLocation(2).WithArguments("Positions", "List"); + + await VerifyCS.VerifyAnalyzerAsync(code, expectedField1, expectedField2, expectedProp); + } + + [Test] + public async Task Negative_UnboundedParameterInRpc() + { + // Verify that unbounded parameters in ServerRpc and ClientRpc trigger warnings + var code = @" +using Mirage; +using System.Collections.Generic; + +public class PlayerBehaviour : NetworkBehaviour +{ + [ServerRpc] + public void CmdSendText(string {|#0:text|}) {} + + [ClientRpc] + public void RpcUpdateList(List {|#1:items|}) {} +} +" + MockDefinitions; + + var expectedParam1 = VerifyCS.Diagnostic("MIRAGE1502").WithLocation(0).WithArguments("text", "String"); + var expectedParam2 = VerifyCS.Diagnostic("MIRAGE1502").WithLocation(1).WithArguments("items", "List"); + + await VerifyCS.VerifyAnalyzerAsync(code, expectedParam1, expectedParam2); + } + } +} diff --git a/Mirage.Analyzers.Tests/UncompressedPrimitiveTests.cs b/Mirage.Analyzers.Tests/UncompressedPrimitiveTests.cs new file mode 100644 index 00000000000..b07ce7bce3c --- /dev/null +++ b/Mirage.Analyzers.Tests/UncompressedPrimitiveTests.cs @@ -0,0 +1,178 @@ +using NUnit.Framework; +using System.Threading.Tasks; + +namespace Mirage.Analyzers.Tests +{ + [TestFixture] + public class UncompressedPrimitiveTests + { + private const string MockDefinitions = @" +namespace Mirage +{ + public class NetworkMessageAttribute : System.Attribute {} + public class NetworkBehaviour {} + public class SyncVarAttribute : System.Attribute {} + public class ServerRpcAttribute : System.Attribute {} + public class ClientRpcAttribute : System.Attribute {} +} +namespace Mirage.Serialization +{ + public class BitCountAttribute : System.Attribute + { + public BitCountAttribute(int bits) {} + } + public class BitCountFromRangeAttribute : System.Attribute {} + public class VarIntAttribute : System.Attribute {} + public class VarIntBlocksAttribute : System.Attribute {} + public class FloatPackAttribute : System.Attribute {} + public class Vector2PackAttribute : System.Attribute {} + public class Vector3PackAttribute : System.Attribute {} + public class QuaternionPackAttribute : System.Attribute {} +} +namespace UnityEngine +{ + public struct Vector2 + { + public float x; + public float y; + } + public struct Vector3 + { + public float x; + public float y; + public float z; + } + public struct Quaternion + { + public float x; + public float y; + public float z; + public float w; + } +} +"; + + [Test] + public async Task Positive_CompressedPrimitivesAndVectors() + { + // Verify that decorated primitive types and Unity vectors do not trigger uncompressed warning + var code = @" +using Mirage; +using Mirage.Serialization; +using UnityEngine; + +[NetworkMessage] +public struct ValidMessage +{ + [BitCount(8)] + public int score; + + [FloatPack] + public float temperature; + + [Vector2Pack] + public Vector2 velocity; + + [Vector3Pack] + public Vector3 position; + + [QuaternionPack] + public Quaternion rotation; +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task Positive_AllowedUncompressedTypes() + { + // Verify that small type primitives like bool, byte, sbyte, char, short, ushort do not require compression + var code = @" +using Mirage; + +[NetworkMessage] +public struct AllowedMessage +{ + public bool isReady; + public byte health; + public char category; + public short level; +} +" + MockDefinitions; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task Negative_UncompressedSyncVars() + { + // Verify that uncompressed SyncVar properties and fields trigger warning + var code = @" +using Mirage; + +public class Player : NetworkBehaviour +{ + [SyncVar] + public int {|#0:Health|} { get; set; } + + [SyncVar] + public float {|#1:PlayerScale|}; +} +" + MockDefinitions; + + var expectedProp = VerifyCS.Diagnostic("MIRAGE1503").WithLocation(0).WithArguments("Health", "Int32"); + var expectedField = VerifyCS.Diagnostic("MIRAGE1503").WithLocation(1).WithArguments("PlayerScale", "Single"); + + await VerifyCS.VerifyAnalyzerAsync(code, expectedProp, expectedField); + } + + [Test] + public async Task Negative_UncompressedRpcParameters() + { + // Verify that uncompressed RPC parameters trigger warning + var code = @" +using Mirage; +using UnityEngine; + +public class Player : NetworkBehaviour +{ + [ServerRpc] + public void CmdUpdateStatus(int {|#0:score|}, float {|#1:val|}) {} + + [ClientRpc] + public void RpcUpdatePhysics(Vector3 {|#2:pos|}, Quaternion {|#3:rot|}) {} +} +" + MockDefinitions; + + var expectedScore = VerifyCS.Diagnostic("MIRAGE1503").WithLocation(0).WithArguments("score", "Int32"); + var expectedVal = VerifyCS.Diagnostic("MIRAGE1503").WithLocation(1).WithArguments("val", "Single"); + var expectedPos = VerifyCS.Diagnostic("MIRAGE1503").WithLocation(2).WithArguments("pos", "Vector3"); + var expectedRot = VerifyCS.Diagnostic("MIRAGE1503").WithLocation(3).WithArguments("rot", "Quaternion"); + + await VerifyCS.VerifyAnalyzerAsync(code, expectedScore, expectedVal, expectedPos, expectedRot); + } + + [Test] + public async Task Negative_UncompressedFieldsInMessage() + { + // Verify that uncompressed fields in a NetworkMessage trigger warning + var code = @" +using Mirage; +using UnityEngine; + +[NetworkMessage] +public struct StatusMessage +{ + public double {|#0:timestamp|}; + public Vector2 {|#1:offset|}; +} +" + MockDefinitions; + + var expectedTimestamp = VerifyCS.Diagnostic("MIRAGE1503").WithLocation(0).WithArguments("timestamp", "Double"); + var expectedOffset = VerifyCS.Diagnostic("MIRAGE1503").WithLocation(1).WithArguments("offset", "Vector2"); + + await VerifyCS.VerifyAnalyzerAsync(code, expectedTimestamp, expectedOffset); + } + } +} diff --git a/Mirage.Analyzers.Tests/VerifyCS.cs b/Mirage.Analyzers.Tests/VerifyCS.cs new file mode 100644 index 00000000000..8f7d3afeea2 --- /dev/null +++ b/Mirage.Analyzers.Tests/VerifyCS.cs @@ -0,0 +1,35 @@ +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.Testing.Verifiers; + +namespace Mirage.Analyzers.Tests +{ + public static class VerifyCS + { + public static DiagnosticResult Diagnostic(string diagnosticId) + { + return CSharpAnalyzerVerifier.Diagnostic(diagnosticId); + } + + public static Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected) + { + var test = new Test + { + TestCode = source, + }; + + test.ExpectedDiagnostics.AddRange(expected); + return test.RunAsync(); + } + + private class Test : CSharpAnalyzerTest + { + public Test() + { + ReferenceAssemblies = ReferenceAssemblies.Default; + } + } + } +} diff --git a/Mirage.Analyzers/.gitignore b/Mirage.Analyzers/.gitignore new file mode 100644 index 00000000000..91b296141e3 --- /dev/null +++ b/Mirage.Analyzers/.gitignore @@ -0,0 +1,2 @@ +!*.csproj +!*.sln diff --git a/Mirage.Analyzers/Mirage.Analyzers.csproj b/Mirage.Analyzers/Mirage.Analyzers.csproj new file mode 100644 index 00000000000..e8bb018ed16 --- /dev/null +++ b/Mirage.Analyzers/Mirage.Analyzers.csproj @@ -0,0 +1,18 @@ + + + netstandard2.0 + 9.0 + enable + false + $(NoWarn);RS2008 + + + + + + + + + + + diff --git a/Mirage.Analyzers/Mirage.Analyzers.sln b/Mirage.Analyzers/Mirage.Analyzers.sln new file mode 100644 index 00000000000..b7d89177dd8 --- /dev/null +++ b/Mirage.Analyzers/Mirage.Analyzers.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.5.11716.220 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mirage.Analyzers", "Mirage.Analyzers.csproj", "{F8AD7409-6FB0-5775-D080-236CB9B32B00}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mirage.Analyzers.Tests", "..\Mirage.Analyzers.Tests\Mirage.Analyzers.Tests.csproj", "{848E7273-08A6-A8DF-F745-3425A250804B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F8AD7409-6FB0-5775-D080-236CB9B32B00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F8AD7409-6FB0-5775-D080-236CB9B32B00}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F8AD7409-6FB0-5775-D080-236CB9B32B00}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F8AD7409-6FB0-5775-D080-236CB9B32B00}.Release|Any CPU.Build.0 = Release|Any CPU + {848E7273-08A6-A8DF-F745-3425A250804B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {848E7273-08A6-A8DF-F745-3425A250804B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {848E7273-08A6-A8DF-F745-3425A250804B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {848E7273-08A6-A8DF-F745-3425A250804B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {FDC630C9-3AAE-4DEC-9F37-4C43126676EB} + EndGlobalSection +EndGlobal diff --git a/Mirage.Analyzers/MirageAnalyzer.Lifecycles.cs b/Mirage.Analyzers/MirageAnalyzer.Lifecycles.cs new file mode 100644 index 00000000000..74cfbd325bc --- /dev/null +++ b/Mirage.Analyzers/MirageAnalyzer.Lifecycles.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections.Generic; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Mirage.Analyzers +{ + public partial class MirageAnalyzer + { + private static void AnalyzeMethodDeclaration(SyntaxNodeAnalysisContext context) + { + var methodDeclaration = (MethodDeclarationSyntax)context.Node; + var methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodDeclaration); + if (methodSymbol == null) + return; + + var containingType = methodSymbol.ContainingType; + if (containingType == null || !MirageTypes.NetworkBehaviour.IsOrInherits(containingType)) + return; + + // MIRAGE1401: Accessing Network State in Awake/Start + if (methodSymbol.Name == "Awake" || methodSymbol.Name == "Start") + { + if (methodDeclaration.Body != null) + { + var walker = new NetworkStateAccessWalker(context.SemanticModel, containingType, context); + walker.Visit(methodDeclaration.Body); + } + } + + // MIRAGE1402: Missing base Call in OnSerialize/OnDeserialize + if (methodSymbol.IsOverride && (methodSymbol.Name == "OnSerialize" || methodSymbol.Name == "OnDeserialize")) + { + bool baseHierarchyHasSyncState = false; + var baseType = containingType.BaseType; + while (baseType != null && !MirageTypes.NetworkBehaviour.Is(baseType) && baseType.ToDisplayString() != "object") + { + if (MirageTypes.HasSynchronizedState(baseType)) + { + baseHierarchyHasSyncState = true; + break; + } + baseType = baseType.BaseType; + } + + if (baseHierarchyHasSyncState) + { + if (methodDeclaration.Body != null) + { + var walker = new BaseCallWalker(context.SemanticModel, methodSymbol.Name); + walker.Visit(methodDeclaration.Body); + if (!walker.HasBaseCall) + { + var diagnostic = Diagnostic.Create(MirageRules.LifecycleMissingBaseCallRule, methodSymbol.Locations[0], methodSymbol.Name); + context.ReportDiagnostic(diagnostic); + } + } + } + } + } + + private class NetworkStateAccessWalker : CSharpSyntaxWalker + { + private readonly SemanticModel _semanticModel; + private readonly INamedTypeSymbol _containingType; + private readonly SyntaxNodeAnalysisContext _context; + + public NetworkStateAccessWalker(SemanticModel semanticModel, INamedTypeSymbol containingType, SyntaxNodeAnalysisContext context) + { + _semanticModel = semanticModel; + _containingType = containingType; + _context = context; + } + + public override void VisitIdentifierName(IdentifierNameSyntax node) + { + CheckNode(node); + base.VisitIdentifierName(node); + } + + public override void VisitMemberAccessExpression(MemberAccessExpressionSyntax node) + { + CheckNode(node); + base.VisitMemberAccessExpression(node); + } + + private void CheckNode(ExpressionSyntax node) + { + var symbol = _semanticModel.GetSymbolInfo(node).Symbol; + if (symbol == null) + return; + + if (symbol is IPropertySymbol propertySymbol) + { + var name = propertySymbol.Name; + if (name == "IsServer" || name == "IsClient" || name == "HasAuthority" || + name == "IsLocalPlayer" || name == "IsOwner" || name == "IsHost") + { + if (MirageTypes.NetworkBehaviour.IsOrInherits(propertySymbol.ContainingType)) + { + var diagnostic = Diagnostic.Create(MirageRules.LifecycleNetworkStateRule, node.GetLocation(), name, _context.ContainingSymbol?.Name ?? "Unknown"); + _context.ReportDiagnostic(diagnostic); + return; + } + } + + if (MirageAttributes.SyncVar.Has(propertySymbol)) + { + var diagnostic = Diagnostic.Create(MirageRules.LifecycleNetworkStateRule, node.GetLocation(), propertySymbol.Name, _context.ContainingSymbol?.Name ?? "Unknown"); + _context.ReportDiagnostic(diagnostic); + return; + } + } + else if (symbol is IFieldSymbol fieldSymbol) + { + if (MirageAttributes.SyncVar.Has(fieldSymbol)) + { + var diagnostic = Diagnostic.Create(MirageRules.LifecycleNetworkStateRule, node.GetLocation(), fieldSymbol.Name, _context.ContainingSymbol?.Name ?? "Unknown"); + _context.ReportDiagnostic(diagnostic); + return; + } + } + } + } + + private class BaseCallWalker : CSharpSyntaxWalker + { + private readonly SemanticModel _semanticModel; + private readonly string _methodName; + public bool HasBaseCall { get; private set; } + + public BaseCallWalker(SemanticModel semanticModel, string methodName) + { + _semanticModel = semanticModel; + _methodName = methodName; + } + + public override void VisitMemberAccessExpression(MemberAccessExpressionSyntax node) + { + if (node.Expression is BaseExpressionSyntax) + { + var symbol = _semanticModel.GetSymbolInfo(node.Name).Symbol; + if (symbol != null && symbol.Name == _methodName) + HasBaseCall = true; + } + base.VisitMemberAccessExpression(node); + } + } + } +} diff --git a/Mirage.Analyzers/MirageAnalyzer.Performance.cs b/Mirage.Analyzers/MirageAnalyzer.Performance.cs new file mode 100644 index 00000000000..37971722b44 --- /dev/null +++ b/Mirage.Analyzers/MirageAnalyzer.Performance.cs @@ -0,0 +1,223 @@ +using System; +using System.Collections.Generic; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Mirage.Analyzers +{ + public partial class MirageAnalyzer + { + private static void AnalyzeFieldPerformance(SymbolAnalysisContext context) + { + var fieldSymbol = (IFieldSymbol)context.Symbol; + + if (fieldSymbol.ContainingType != null && MirageAttributes.NetworkMessage.Has(fieldSymbol.ContainingType)) + { + if (IsUnboundedCollectionOrString(fieldSymbol, fieldSymbol.Type)) + { + var diagnostic = Diagnostic.Create(MirageRules.PerformanceUnboundedCollectionRule, fieldSymbol.Locations[0], fieldSymbol.Name, fieldSymbol.Type.Name); + context.ReportDiagnostic(diagnostic); + } + + CheckHighOverhead(context, fieldSymbol, fieldSymbol.Type); + } + + if (MirageAttributes.SyncVar.Has(fieldSymbol)) + CheckHighOverhead(context, fieldSymbol, fieldSymbol.Type); + } + + private static void AnalyzePropertyPerformance(SymbolAnalysisContext context) + { + var propertySymbol = (IPropertySymbol)context.Symbol; + + if (propertySymbol.ContainingType != null && MirageAttributes.NetworkMessage.Has(propertySymbol.ContainingType)) + { + if (IsUnboundedCollectionOrString(propertySymbol, propertySymbol.Type)) + { + var diagnostic = Diagnostic.Create(MirageRules.PerformanceUnboundedCollectionRule, propertySymbol.Locations[0], propertySymbol.Name, propertySymbol.Type.Name); + context.ReportDiagnostic(diagnostic); + } + + CheckHighOverhead(context, propertySymbol, propertySymbol.Type); + } + + if (MirageAttributes.SyncVar.Has(propertySymbol)) + CheckHighOverhead(context, propertySymbol, propertySymbol.Type); + } + + private static void AnalyzeParameterPerformance(SymbolAnalysisContext context) + { + var parameterSymbol = (IParameterSymbol)context.Symbol; + var containingMethod = parameterSymbol.ContainingSymbol as IMethodSymbol; + + if (containingMethod != null && IsRpcMethod(containingMethod)) + { + if (IsUnboundedCollectionOrString(parameterSymbol, parameterSymbol.Type)) + { + var diagnostic = Diagnostic.Create(MirageRules.PerformanceUnboundedCollectionRule, parameterSymbol.Locations[0], parameterSymbol.Name, parameterSymbol.Type.Name); + context.ReportDiagnostic(diagnostic); + } + + CheckHighOverhead(context, parameterSymbol, parameterSymbol.Type); + } + } + + private static void AnalyzeNamedTypePerformance(SymbolAnalysisContext context) + { + var typeSymbol = (INamedTypeSymbol)context.Symbol; + + if (MirageAttributes.NetworkMessage.Has(typeSymbol)) + { + var visited = new HashSet(SymbolEqualityComparer.Default); + int estimatedSize = EstimateSerializedSize(typeSymbol, visited); + if (estimatedSize > 1200) + { + var diagnostic = Diagnostic.Create(MirageRules.PerformanceMtuExceededRule, typeSymbol.Locations[0], typeSymbol.Name, estimatedSize, 1200); + context.ReportDiagnostic(diagnostic); + } + } + } + + private static bool IsUnboundedCollectionOrString(ISymbol symbol, ITypeSymbol type) + { + bool isString = type.SpecialType == SpecialType.System_String; + bool isCollection = type.TypeKind == TypeKind.Array || + (type is INamedTypeSymbol namedType && MirageTypes.IEnumerable.Implements(namedType)); + + if (!isString && !isCollection) + return false; + + if (MirageAttributes.HasCompressionAttribute(symbol, type)) + return false; + + return true; + } + + private static void CheckHighOverhead(SymbolAnalysisContext context, ISymbol symbol, ITypeSymbol type) + { + bool isOverheadType = type.SpecialType == SpecialType.System_Int32 || + type.SpecialType == SpecialType.System_UInt32 || + type.SpecialType == SpecialType.System_Int64 || + type.SpecialType == SpecialType.System_UInt64 || + type.SpecialType == SpecialType.System_Single || + type.SpecialType == SpecialType.System_Double || + MirageTypes.Vector2.Is(type) || + MirageTypes.Vector3.Is(type) || + MirageTypes.Quaternion.Is(type); + + if (isOverheadType && !MirageAttributes.HasCompressionAttribute(symbol, type)) + { + var diagnostic = Diagnostic.Create(MirageRules.PerformanceHighOverheadRule, symbol.Locations[0], symbol.Name, type.Name); + context.ReportDiagnostic(diagnostic); + } + } + + private static int EstimateSerializedSize(ITypeSymbol type, HashSet visited) + { + if (type == null) + return 0; + + if (!visited.Add(type)) + return 0; + + if (MirageTypes.GameObject.IsOrInherits(type) || + MirageTypes.NetworkBehaviour.IsOrInherits(type) || + MirageTypes.NetworkIdentity.IsOrInherits(type)) + { + return 2; + } + + switch (type.SpecialType) + { + case SpecialType.System_Boolean: + case SpecialType.System_Byte: + case SpecialType.System_SByte: + return 1; + case SpecialType.System_Char: + return 2; + case SpecialType.System_Int16: + case SpecialType.System_UInt16: + return 2; + case SpecialType.System_Int32: + case SpecialType.System_UInt32: + return 1; // 1 byte default for typical uint/int + case SpecialType.System_Int64: + case SpecialType.System_UInt64: + return 8; + case SpecialType.System_Single: + return 4; + case SpecialType.System_Double: + return 8; + case SpecialType.System_Decimal: + return 16; + case SpecialType.System_String: + return 32; + } + + if (type.ContainingNamespace?.ToDisplayString() == "UnityEngine") + { + switch (type.Name) + { + case "Vector2": return 8; + case "Vector3": return 12; + case "Vector4": return 16; + case "Quaternion": return 16; + case "Color": return 16; + case "Color32": return 4; + case "Vector2Int": return 2; + case "Vector3Int": return 3; + } + } + + if (type.TypeKind == TypeKind.Enum) + { + var underlying = (type as INamedTypeSymbol)?.EnumUnderlyingType; + return underlying != null ? EstimateSerializedSize(underlying, visited) : 1; + } + + if (type is IArrayTypeSymbol arrayType) + return 128 * EstimateSerializedSize(arrayType.ElementType, visited); + + if (type is INamedTypeSymbol namedType && namedType.IsGenericType) + { + if (MirageTypes.IEnumerable.Implements(namedType)) + { + var elemType = namedType.TypeArguments.Length > 0 ? namedType.TypeArguments[0] : null; + if (elemType != null) + return 128 * EstimateSerializedSize(elemType, visited); + } + } + + if (type.TypeKind == TypeKind.Struct || type.TypeKind == TypeKind.Class) + { + int sum = 0; + foreach (var member in type.GetMembers()) + { + if (member is IFieldSymbol field && !field.IsStatic && field.DeclaredAccessibility == Accessibility.Public) + sum += EstimateMemberSerializedSize(field, field.Type, visited); + } + return sum; + } + + return 0; + } + + private static int EstimateMemberSerializedSize(ISymbol symbol, ITypeSymbol type, HashSet visited) + { + if (MirageAttributes.BitCount.TryGet(symbol, out var bitCountAttr) && bitCountAttr.ConstructorArguments.Length > 0) + if (bitCountAttr.ConstructorArguments[0].Value is int bits) + return (bits + 7) / 8; + + if (MirageAttributes.FloatPack.TryGet(symbol, out var floatPackAttr)) + { + if (floatPackAttr.ConstructorArguments.Length >= 2) + if (floatPackAttr.ConstructorArguments[1].Value is int bits) + return (bits + 7) / 8; + + return 4; + } + + return EstimateSerializedSize(type, visited); + } + } +} diff --git a/Mirage.Analyzers/MirageAnalyzer.Rpcs.cs b/Mirage.Analyzers/MirageAnalyzer.Rpcs.cs new file mode 100644 index 00000000000..3948b501911 --- /dev/null +++ b/Mirage.Analyzers/MirageAnalyzer.Rpcs.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Mirage.Analyzers +{ + public partial class MirageAnalyzer + { + private static void AnalyzeMethodRpcs(SymbolAnalysisContext context) + { + var methodSymbol = (IMethodSymbol)context.Symbol; + + AnalyzeNetworkAttributes(context, methodSymbol); + + if (IsRpcMethod(methodSymbol)) + { + if (methodSymbol.IsGenericMethod) + { + var diagnostic = Diagnostic.Create(MirageRules.RpcSignatureRule, methodSymbol.Locations[0], methodSymbol.Name, "cannot have generic parameters"); + context.ReportDiagnostic(diagnostic); + } + + if (!IsVoidOrUniTask(methodSymbol.ReturnType)) + { + var diagnostic = Diagnostic.Create(MirageRules.RpcSignatureRule, methodSymbol.Locations[0], methodSymbol.Name, $"cannot return '{methodSymbol.ReturnType.ToDisplayString()}' (must return void or UniTask)"); + context.ReportDiagnostic(diagnostic); + } + + // MIRAGE1202: RPC parameters cannot have ref/out parameter modifiers + foreach (var param in methodSymbol.Parameters) + { + if (param.RefKind == RefKind.Ref || param.RefKind == RefKind.Out) + { + var diagnostic = Diagnostic.Create(MirageRules.RpcRefOutRule, param.Locations[0], methodSymbol.Name, param.Name); + context.ReportDiagnostic(diagnostic); + } + } + + // MIRAGE1203: Static RPC Methods + if (methodSymbol.IsStatic) + { + var diagnostic = Diagnostic.Create(MirageRules.RpcStaticRule, methodSymbol.Locations[0], methodSymbol.Name); + context.ReportDiagnostic(diagnostic); + } + + // MIRAGE1204: Invalid ClientRpc Target Configurations + if (MirageAttributes.ClientRpc.TryGet(methodSymbol, out var clientRpcAttr)) + { + int targetVal = 1; // Default is RpcTarget.Observers (1) + MirageAttributes.ClientRpc.TryGetNamedArgument(clientRpcAttr, "target", out targetVal); + + if (targetVal == 1 && !methodSymbol.ReturnsVoid) + { + var diagnostic = Diagnostic.Create(MirageRules.ClientRpcTargetRule, methodSymbol.Locations[0], methodSymbol.Name, "must return void when target is Observers"); + context.ReportDiagnostic(diagnostic); + } + + if (targetVal == 2) + { + bool firstParamIsValid = false; + if (methodSymbol.Parameters.Length > 0) + { + var firstParamType = methodSymbol.Parameters[0].Type; + if (MirageTypes.IsNetworkPlayerOrConnection(firstParamType)) + firstParamIsValid = true; + } + if (!firstParamIsValid) + { + var diagnostic = Diagnostic.Create(MirageRules.ClientRpcTargetRule, methodSymbol.Locations[0], methodSymbol.Name, "method with target = Player requires first parameter to be INetworkPlayer or NetworkConnection"); + context.ReportDiagnostic(diagnostic); + } + } + } + + // MIRAGE1205: Invalid RateLimit Attribute Settings + bool hasRateLimit = MirageAttributes.RateLimit.TryGet(methodSymbol, out var rateLimitAttr); + if (hasRateLimit) + { + float interval = 1f; + int refill = 50; + int maxTokens = 200; + MirageAttributes.RateLimit.TryGetNamedArgument(rateLimitAttr, "Interval", out interval); + MirageAttributes.RateLimit.TryGetNamedArgument(rateLimitAttr, "Refill", out refill); + MirageAttributes.RateLimit.TryGetNamedArgument(rateLimitAttr, "MaxTokens", out maxTokens); + + var errors = new List(); + if (interval <= 0) + errors.Add("Interval must be greater than zero"); + if (refill <= 0) + errors.Add("Refill must be greater than zero"); + if (maxTokens <= 0) + errors.Add("MaxTokens must be greater than zero"); + if (maxTokens > 0 && refill > 0 && maxTokens < refill) + errors.Add("MaxTokens must be greater than or equal to Refill"); + + if (errors.Count > 0) + { + var diagnostic = Diagnostic.Create(MirageRules.RateLimitSettingsRule, methodSymbol.Locations[0], methodSymbol.Name, string.Join(", ", errors)); + context.ReportDiagnostic(diagnostic); + } + } + + // MIRAGE1206: Missing RateLimit on ServerRpc + if (MirageAttributes.ServerRpc.TryGet(methodSymbol, out _) && !hasRateLimit) + { + var diagnostic = Diagnostic.Create(MirageRules.ServerRpcMissingRateLimitRule, methodSymbol.Locations[0], methodSymbol.Name); + context.ReportDiagnostic(diagnostic); + } + } + } + + private static void AnalyzeParameterRpcs(SymbolAnalysisContext context) + { + // RPC parameter validation is handled inside AnalyzeMethodRpcs. + } + } +} diff --git a/Mirage.Analyzers/MirageAnalyzer.Serialization.cs b/Mirage.Analyzers/MirageAnalyzer.Serialization.cs new file mode 100644 index 00000000000..28f2654d114 --- /dev/null +++ b/Mirage.Analyzers/MirageAnalyzer.Serialization.cs @@ -0,0 +1,231 @@ +using System; +using System.Collections.Generic; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Mirage.Analyzers +{ + public partial class MirageAnalyzer + { + private static void AnalyzeFieldSerialization(SymbolAnalysisContext context, CustomSerializers serializers) + { + var fieldSymbol = (IFieldSymbol)context.Symbol; + + if (fieldSymbol.ContainingType != null && MirageAttributes.NetworkMessage.Has(fieldSymbol.ContainingType)) + { + AnalyzeMessageOrRpc(context, fieldSymbol, fieldSymbol.Type, "NetworkMessage field"); + + var visited = new HashSet(SymbolEqualityComparer.Default); + if (!IsTypeSerializable(context.Compilation, fieldSymbol.Type, serializers, visited)) + { + var diagnostic = Diagnostic.Create(MirageRules.FieldTypeSerializationRule, fieldSymbol.Locations[0], fieldSymbol.Type.Name, "NetworkMessage field"); + context.ReportDiagnostic(diagnostic); + } + } + } + + private static void AnalyzePropertySerialization(SymbolAnalysisContext context, CustomSerializers serializers) + { + var propertySymbol = (IPropertySymbol)context.Symbol; + + if (propertySymbol.ContainingType != null && MirageAttributes.NetworkMessage.Has(propertySymbol.ContainingType)) + { + AnalyzeMessageOrRpc(context, propertySymbol, propertySymbol.Type, "NetworkMessage property"); + + var visited = new HashSet(SymbolEqualityComparer.Default); + if (!IsTypeSerializable(context.Compilation, propertySymbol.Type, serializers, visited)) + { + var diagnostic = Diagnostic.Create(MirageRules.FieldTypeSerializationRule, propertySymbol.Locations[0], propertySymbol.Type.Name, "NetworkMessage property"); + context.ReportDiagnostic(diagnostic); + } + } + } + + private static void AnalyzeMethodSerialization(SymbolAnalysisContext context, CustomSerializers serializers) + { + var methodSymbol = (IMethodSymbol)context.Symbol; + + if (IsRpcMethod(methodSymbol)) + { + var returnType = methodSymbol.ReturnType as INamedTypeSymbol; + if (returnType != null && returnType.IsGenericType && returnType.TypeArguments.Length > 0) + { + var originalDefinition = returnType.OriginalDefinition; + if (originalDefinition != null && MirageTypes.UniTask.Is(returnType)) + { + var typeArgument = returnType.TypeArguments[0]; + AnalyzeMessageOrRpc(context, methodSymbol, typeArgument, "RPC return type"); + + var visited = new HashSet(SymbolEqualityComparer.Default); + if (!IsTypeSerializable(context.Compilation, typeArgument, serializers, visited)) + { + var diagnostic = Diagnostic.Create(MirageRules.FieldTypeSerializationRule, methodSymbol.Locations[0], typeArgument.Name, "RPC return type"); + context.ReportDiagnostic(diagnostic); + } + } + } + } + } + + private static void AnalyzeParameterSerialization(SymbolAnalysisContext context, CustomSerializers serializers) + { + var parameterSymbol = (IParameterSymbol)context.Symbol; + var containingMethod = parameterSymbol.ContainingSymbol as IMethodSymbol; + + if (containingMethod != null && IsRpcMethod(containingMethod)) + { + AnalyzeMessageOrRpc(context, parameterSymbol, parameterSymbol.Type, "RPC parameter"); + + var visited = new HashSet(SymbolEqualityComparer.Default); + if (!IsTypeSerializable(context.Compilation, parameterSymbol.Type, serializers, visited)) + { + var diagnostic = Diagnostic.Create(MirageRules.FieldTypeSerializationRule, parameterSymbol.Locations[0], parameterSymbol.Type.Name, "RPC parameter"); + context.ReportDiagnostic(diagnostic); + } + } + } + + private static void ReportMismatchedSerialization(CompilationAnalysisContext context, CustomSerializers serializers) + { + foreach (var kvp in serializers.Writers) + { + var type = kvp.Key; + var method = kvp.Value; + if (!serializers.Readers.ContainsKey(type)) + { + var diagnostic = Diagnostic.Create(MirageRules.MismatchedSerializationRule, method.Locations[0], type.Name, $"Custom writer defined for '{type.Name}' but matching custom reader is missing."); + context.ReportDiagnostic(diagnostic); + } + } + + foreach (var kvp in serializers.Readers) + { + var type = kvp.Key; + var method = kvp.Value; + if (!serializers.Writers.ContainsKey(type)) + { + var diagnostic = Diagnostic.Create(MirageRules.MismatchedSerializationRule, method.Locations[0], type.Name, $"Custom reader defined for '{type.Name}' but matching custom writer is missing."); + context.ReportDiagnostic(diagnostic); + } + } + } + + private static void AnalyzeMessageOrRpc(SymbolAnalysisContext context, ISymbol symbol, ITypeSymbol typeSymbol, string locationDescription) + { + if (IsBasicSafeType(typeSymbol)) + return; + + if (IsExplicitlyMarkedSafe(symbol, typeSymbol)) + return; + + var diagnostic = Diagnostic.Create(MirageRules.MessageOrRpcRule, symbol.Locations[0], locationDescription, symbol.Name, typeSymbol.Name); + context.ReportDiagnostic(diagnostic); + } + + private static bool IsTypeSerializable(Compilation compilation, ITypeSymbol type, CustomSerializers serializers, HashSet visited) + { + if (type == null) + return true; + + if (type.SpecialType != SpecialType.None) + { + if (type.SpecialType == SpecialType.System_Void) + return false; + return true; + } + + if (type.TypeKind == TypeKind.Enum) + return true; + + if (MirageTypes.NetworkBehaviour.IsOrInherits(type) || + MirageTypes.NetworkIdentity.IsOrInherits(type) || + MirageTypes.GameObject.IsOrInherits(type)) + { + return true; + } + + if (serializers.Writers.ContainsKey(type) && serializers.Readers.ContainsKey(type)) + return true; + + if (type.ContainingNamespace?.ToDisplayString() == "UnityEngine") + { + switch (type.Name) + { + case "Vector2": + case "Vector3": + case "Vector4": + case "Vector2Int": + case "Vector3Int": + case "Color": + case "Color32": + case "Rect": + case "Plane": + case "Ray": + case "Matrix4x4": + case "Quaternion": + return true; + } + } + + if (type.ContainingNamespace?.ToDisplayString() == "System") + { + if (type.Name == "Guid" || type.Name == "DateTime") + return true; + } + + if (type is IArrayTypeSymbol arrayType) + { + if (arrayType.Rank > 1) + return false; + return IsTypeSerializable(compilation, arrayType.ElementType, serializers, visited); + } + + if (type is INamedTypeSymbol namedType) + { + if (MirageTypes.IEnumerable.Implements(namedType)) + { + if (namedType.TypeArguments.Length > 0) + { + foreach (var arg in namedType.TypeArguments) + if (!IsTypeSerializable(compilation, arg, serializers, visited)) + return false; + + return true; + } + return false; + } + + if (namedType.IsGenericType) + { + foreach (var arg in namedType.TypeArguments) + { + if (arg.TypeKind == TypeKind.TypeParameter) + return false; + } + } + + if (namedType.TypeKind == TypeKind.Struct || namedType.TypeKind == TypeKind.Class) + { + if (!visited.Add(namedType)) + return false; + + var ns = namedType.ContainingNamespace?.ToDisplayString(); + if (ns != null && (ns.StartsWith("System") || ns.StartsWith("UnityEngine"))) + return false; + + foreach (var member in namedType.GetMembers()) + { + if (member is IFieldSymbol field && !field.IsStatic && field.DeclaredAccessibility == Accessibility.Public) + { + if (!IsTypeSerializable(compilation, field.Type, serializers, visited)) + return false; + } + } + return true; + } + } + + return false; + } + } +} diff --git a/Mirage.Analyzers/MirageAnalyzer.SyncVars.cs b/Mirage.Analyzers/MirageAnalyzer.SyncVars.cs new file mode 100644 index 00000000000..f3517847f8d --- /dev/null +++ b/Mirage.Analyzers/MirageAnalyzer.SyncVars.cs @@ -0,0 +1,166 @@ +using System; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Mirage.Analyzers +{ + public partial class MirageAnalyzer + { + private static void AnalyzeFieldSyncVars(SymbolAnalysisContext context) + { + var fieldSymbol = (IFieldSymbol)context.Symbol; + + // Reassigning fields implementing ISyncObject is prohibited because doing so replaces the synchronized collection instance, breaking replication and weavers' internal reference tracking. + // SyncObject fields must be readonly to guarantee their references remain constant after initialization, avoiding desynchronization. + if (MirageTypes.ISyncObject.Implements(fieldSymbol.Type) && !fieldSymbol.IsReadOnly) + { + var diagnostic = Diagnostic.Create(MirageRules.ReassignmentRule, fieldSymbol.Locations[0], fieldSymbol.Name); + context.ReportDiagnostic(diagnostic); + } + + AnalyzeNetworkAttributes(context, fieldSymbol); + + if (MirageAttributes.SyncVar.Has(fieldSymbol)) + { + AnalyzeSyncVar(context, fieldSymbol, fieldSymbol.Type); + return; + } + + if (MirageTypes.ISyncObject.Implements(fieldSymbol.Type)) + { + AnalyzeSyncObject(context, fieldSymbol, fieldSymbol.Type); + return; + } + } + + private static void AnalyzePropertySyncVars(SymbolAnalysisContext context) + { + var propertySymbol = (IPropertySymbol)context.Symbol; + + AnalyzeNetworkAttributes(context, propertySymbol); + + if (MirageAttributes.SyncVar.Has(propertySymbol)) + { + if (propertySymbol.GetMethod == null || propertySymbol.SetMethod == null || propertySymbol.IsStatic || !IsAutoProperty(propertySymbol)) + { + var diagnostic = Diagnostic.Create(MirageRules.AutoPropertyRule, propertySymbol.Locations[0], propertySymbol.Name); + context.ReportDiagnostic(diagnostic); + return; + } + + AnalyzeSyncVar(context, propertySymbol, propertySymbol.Type); + return; + } + + if (MirageTypes.ISyncObject.Implements(propertySymbol.Type)) + { + AnalyzeSyncObject(context, propertySymbol, propertySymbol.Type); + return; + } + } + + private static void AnalyzeSyncVar(SymbolAnalysisContext context, ISymbol symbol, ITypeSymbol typeSymbol) + { + if (IsBasicSafeType(typeSymbol)) + return; + + if (IsExplicitlyMarkedSafe(symbol, typeSymbol)) + return; + + var diagnostic = Diagnostic.Create(MirageRules.SyncVarRule, symbol.Locations[0], symbol.Name, typeSymbol.Name); + context.ReportDiagnostic(diagnostic); + } + + private static void AnalyzeSyncObject(SymbolAnalysisContext context, ISymbol symbol, ITypeSymbol typeSymbol) + { + var current = typeSymbol; + while (current != null) + { + if (current is INamedTypeSymbol namedType && namedType.IsGenericType && MirageTypes.ISyncObject.Implements(namedType)) + { + foreach (var arg in namedType.TypeArguments) + AnalyzeSyncVar(context, symbol, arg); + break; + } + current = current.BaseType; + } + } + + private static void AnalyzeMutation(SyntaxNodeAnalysisContext context) + { + var node = context.Node; + var target = GetMutationTarget(node); + if (target == null) + return; + + var symbolInfo = context.SemanticModel.GetSymbolInfo(target); + var symbol = symbolInfo.Symbol; + + if (symbol is IFieldSymbol fieldSymbol && MirageTypes.ISyncObject.Implements(fieldSymbol.Type) && !IsInsideConstructor(node)) + { + var diagnostic = Diagnostic.Create(MirageRules.ReassignmentRule, target.GetLocation(), fieldSymbol.Name); + context.ReportDiagnostic(diagnostic); + } + + // Mutating members of elements inside SyncList or SyncDictionary directly does not trigger serialization or synchronization because the collection has no mechanism to detect nested modifications. + if (target is MemberAccessExpressionSyntax memberAccess) + { + var current = memberAccess.Expression; + while (current is MemberAccessExpressionSyntax nestedMember) + current = nestedMember.Expression; + + if (current is ElementAccessExpressionSyntax elementAccess) + { + var collectionType = context.SemanticModel.GetTypeInfo(elementAccess.Expression).Type; + if (collectionType != null && IsSyncListOrDictionary(collectionType)) + { + var diagnostic = Diagnostic.Create(MirageRules.DirectMutationRule, target.GetLocation(), elementAccess.Expression.ToString()); + context.ReportDiagnostic(diagnostic); + } + } + } + } + + private static ExpressionSyntax? GetMutationTarget(SyntaxNode node) + { + if (node is AssignmentExpressionSyntax assignment) + return assignment.Left; + + if (node is PostfixUnaryExpressionSyntax postfix && (postfix.IsKind(SyntaxKind.PostIncrementExpression) || postfix.IsKind(SyntaxKind.PostDecrementExpression))) + return postfix.Operand; + + if (node is PrefixUnaryExpressionSyntax prefix && (prefix.IsKind(SyntaxKind.PreIncrementExpression) || prefix.IsKind(SyntaxKind.PreDecrementExpression))) + return prefix.Operand; + + if (node is ArgumentSyntax argument && (argument.RefOrOutKeyword.IsKind(SyntaxKind.RefKeyword) || argument.RefOrOutKeyword.IsKind(SyntaxKind.OutKeyword))) + return argument.Expression; + + return null; + } + + private static bool IsInsideConstructor(SyntaxNode node) + { + var current = node.Parent; + while (current != null) + { + if (current is AnonymousFunctionExpressionSyntax || current is LocalFunctionStatementSyntax) + return false; + + if (current is ConstructorDeclarationSyntax) + return true; + + current = current.Parent; + } + return false; + } + + private static bool IsSyncListOrDictionary(ITypeSymbol typeSymbol) + { + return MirageTypes.SyncList.IsOrInherits(typeSymbol) || + MirageTypes.SyncDictionary.IsOrInherits(typeSymbol) || + MirageTypes.SyncIDictionary.IsOrInherits(typeSymbol); + } + } +} diff --git a/Mirage.Analyzers/MirageAnalyzer.cs b/Mirage.Analyzers/MirageAnalyzer.cs new file mode 100644 index 00000000000..7edc1002b8f --- /dev/null +++ b/Mirage.Analyzers/MirageAnalyzer.cs @@ -0,0 +1,247 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Mirage.Analyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public partial class MirageAnalyzer : DiagnosticAnalyzer + { + + + + public override ImmutableArray SupportedDiagnostics => MirageRules.SupportedDiagnostics; + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterCompilationStartAction(compilationContext => + { + var customSerializers = FindCustomWritersAndReaders(compilationContext.Compilation); + + compilationContext.RegisterSymbolAction(symbolContext => AnalyzeField(symbolContext, customSerializers), SymbolKind.Field); + compilationContext.RegisterSymbolAction(symbolContext => AnalyzeProperty(symbolContext, customSerializers), SymbolKind.Property); + compilationContext.RegisterSymbolAction(symbolContext => AnalyzeMethod(symbolContext, customSerializers), SymbolKind.Method); + compilationContext.RegisterSymbolAction(symbolContext => AnalyzeParameter(symbolContext, customSerializers), SymbolKind.Parameter); + compilationContext.RegisterSymbolAction(AnalyzeNamedType, SymbolKind.NamedType); + + compilationContext.RegisterSyntaxNodeAction(AnalyzeMethodDeclaration, SyntaxKind.MethodDeclaration); + + compilationContext.RegisterSyntaxNodeAction(AnalyzeMutation, + SyntaxKind.SimpleAssignmentExpression, + SyntaxKind.AddAssignmentExpression, + SyntaxKind.SubtractAssignmentExpression, + SyntaxKind.MultiplyAssignmentExpression, + SyntaxKind.DivideAssignmentExpression, + SyntaxKind.ModuloAssignmentExpression, + SyntaxKind.AndAssignmentExpression, + SyntaxKind.ExclusiveOrAssignmentExpression, + SyntaxKind.OrAssignmentExpression, + SyntaxKind.LeftShiftAssignmentExpression, + SyntaxKind.RightShiftAssignmentExpression, + SyntaxKind.CoalesceAssignmentExpression, + SyntaxKind.PostIncrementExpression, + SyntaxKind.PostDecrementExpression, + SyntaxKind.PreIncrementExpression, + SyntaxKind.PreDecrementExpression, + SyntaxKind.Argument); + + compilationContext.RegisterCompilationEndAction(endContext => + { + ReportMismatchedSerialization(endContext, customSerializers); + }); + }); + } + + private static void AnalyzeField(SymbolAnalysisContext context, CustomSerializers serializers) + { + AnalyzeFieldSyncVars(context); + AnalyzeFieldSerialization(context, serializers); + AnalyzeFieldPerformance(context); + } + + private static void AnalyzeProperty(SymbolAnalysisContext context, CustomSerializers serializers) + { + AnalyzePropertySyncVars(context); + AnalyzePropertySerialization(context, serializers); + AnalyzePropertyPerformance(context); + } + + private static void AnalyzeMethod(SymbolAnalysisContext context, CustomSerializers serializers) + { + AnalyzeMethodRpcs(context); + AnalyzeMethodSerialization(context, serializers); + } + + private static void AnalyzeParameter(SymbolAnalysisContext context, CustomSerializers serializers) + { + AnalyzeParameterRpcs(context); + AnalyzeParameterSerialization(context, serializers); + AnalyzeParameterPerformance(context); + } + + private static void AnalyzeNamedType(SymbolAnalysisContext context) + { + AnalyzeNamedTypePerformance(context); + } + + public class CustomSerializers + { + public Dictionary Writers { get; } = new Dictionary(SymbolEqualityComparer.Default); + public Dictionary Readers { get; } = new Dictionary(SymbolEqualityComparer.Default); + } + + private static CustomSerializers FindCustomWritersAndReaders(Compilation compilation) + { + var serializers = new CustomSerializers(); + VisitNamespace(compilation.Assembly.GlobalNamespace, serializers); + return serializers; + } + + private static void VisitNamespace(INamespaceSymbol ns, CustomSerializers serializers) + { + foreach (var member in ns.GetMembers()) + { + if (member is INamespaceSymbol nestedNs) + VisitNamespace(nestedNs, serializers); + else if (member is INamedTypeSymbol type) + VisitType(type, serializers); + } + } + + private static void VisitType(INamedTypeSymbol type, CustomSerializers serializers) + { + if (type.IsStatic) + { + foreach (var member in type.GetMembers()) + { + if (member is IMethodSymbol method && method.IsStatic && method.IsExtensionMethod) + { + if (method.Parameters.Length == 2 && + IsNetworkWriter(method.Parameters[0].Type) && + method.ReturnsVoid) + { + serializers.Writers[method.Parameters[1].Type] = method; + } + else if (method.Parameters.Length == 1 && + IsNetworkReader(method.Parameters[0].Type) && + !method.ReturnsVoid) + { + serializers.Readers[method.ReturnType] = method; + } + } + } + } + + foreach (var nestedType in type.GetTypeMembers()) + VisitType(nestedType, serializers); + } + + private static bool IsNetworkWriter(ITypeSymbol type) + { + return MirageTypes.NetworkWriter.Is(type); + } + + private static bool IsNetworkReader(ITypeSymbol type) + { + return MirageTypes.NetworkReader.Is(type); + } + + private static void AnalyzeNetworkAttributes(SymbolAnalysisContext context, ISymbol symbol) + { + var containingType = symbol.ContainingType; + + // Stop analysis if the declaring type inherits from NetworkBehaviour, as network attributes are valid in this context. + if (containingType != null && MirageTypes.NetworkBehaviour.IsOrInherits(containingType)) + return; + + foreach (var attr in symbol.GetAttributes()) + { + if (attr.AttributeClass != null) + { + foreach (var networkAttr in MirageAttributes.NetworkAttributes) + { + if (networkAttr.Matches(attr.AttributeClass)) + { + var location = attr.ApplicationSyntaxReference?.GetSyntax()?.GetLocation() ?? symbol.Locations[0]; + var diagnostic = Diagnostic.Create(MirageRules.NetworkBehaviourAttributeRule, location, attr.AttributeClass.Name, symbol.Name); + context.ReportDiagnostic(diagnostic); + break; + } + } + } + } + } + + private static bool IsBasicSafeType(ITypeSymbol typeSymbol) + { + if (typeSymbol == null) + return true; + + if (typeSymbol.IsValueType || typeSymbol.TypeKind == TypeKind.Struct || typeSymbol.TypeKind == TypeKind.Enum) + return true; + + if (typeSymbol.SpecialType == SpecialType.System_String) + return true; + + if (MirageTypes.NetworkIdentity.IsOrInherits(typeSymbol)) + return true; + if (MirageTypes.GameObject.IsOrInherits(typeSymbol)) + return true; + if (MirageTypes.NetworkBehaviour.IsOrInherits(typeSymbol)) + return true; + + return false; + } + + private static bool IsExplicitlyMarkedSafe(ISymbol symbol, ITypeSymbol typeSymbol) + { + if (MirageAttributes.WeaverSafeClass.Has(symbol)) + return true; + + if (typeSymbol != null && MirageAttributes.WeaverSafeClass.Has(typeSymbol)) + return true; + + if (symbol.ContainingType != null && MirageAttributes.WeaverSafeClass.Has(symbol.ContainingType)) + return true; + + return false; + } + + private static bool IsRpcMethod(IMethodSymbol methodSymbol) + { + return MirageAttributes.ServerRpc.Has(methodSymbol) || MirageAttributes.ClientRpc.Has(methodSymbol); + } + + private static bool IsVoidOrUniTask(ITypeSymbol typeSymbol) + { + if (typeSymbol == null) + return false; + + if (typeSymbol.SpecialType == SpecialType.System_Void) + return true; + + if (MirageTypes.UniTask.Is(typeSymbol)) + return true; + + return false; + } + + private static bool IsAutoProperty(IPropertySymbol propertySymbol) + { + var backingFieldName = $"<{propertySymbol.Name}>k__BackingField"; + foreach (var member in propertySymbol.ContainingType.GetMembers()) + { + if (member is IFieldSymbol field && field.IsImplicitlyDeclared && field.Name == backingFieldName) + return true; + } + return false; + } + } +} diff --git a/Mirage.Analyzers/MirageRules.cs b/Mirage.Analyzers/MirageRules.cs new file mode 100644 index 00000000000..706ad445414 --- /dev/null +++ b/Mirage.Analyzers/MirageRules.cs @@ -0,0 +1,240 @@ +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; + +namespace Mirage.Analyzers +{ + public static class MirageRules + { + public const string SyncVarDiagnosticId = "MIRAGE1001"; + public const string AutoPropertyDiagnosticId = "MIRAGE1002"; + public const string DirectMutationDiagnosticId = "MIRAGE1003"; + public const string ReassignmentDiagnosticId = "MIRAGE1004"; + public const string NetworkBehaviourAttributeDiagnosticId = "MIRAGE1101"; + public const string RpcSignatureDiagnosticId = "MIRAGE1201"; + public const string RpcRefOutDiagnosticId = "MIRAGE1202"; + public const string RpcStaticDiagnosticId = "MIRAGE1203"; + public const string ClientRpcTargetDiagnosticId = "MIRAGE1204"; + public const string RateLimitSettingsDiagnosticId = "MIRAGE1205"; + public const string ServerRpcMissingRateLimitDiagnosticId = "MIRAGE1206"; + public const string MessageOrRpcDiagnosticId = "MIRAGE1301"; + public const string FieldTypeSerializationDiagnosticId = "MIRAGE1302"; + public const string MismatchedSerializationDiagnosticId = "MIRAGE1303"; + public const string LifecycleNetworkStateDiagnosticId = "MIRAGE1401"; + public const string LifecycleMissingBaseCallDiagnosticId = "MIRAGE1402"; + public const string PerformanceMtuExceededDiagnosticId = "MIRAGE1501"; + public const string PerformanceUnboundedCollectionDiagnosticId = "MIRAGE1502"; + public const string PerformanceHighOverheadDiagnosticId = "MIRAGE1503"; + + public static readonly DiagnosticDescriptor SyncVarRule = new DiagnosticDescriptor( + SyncVarDiagnosticId, + "SyncVar cannot be a class type unless marked safe", + "SyncVar or SyncObject '{0}' is or contains class type '{1}'. Class-based SyncVars/SyncObjects allocate memory, do not support polymorphism (only declared fields serialize), and cannot track internal changes automatically (meaning modifications won't trigger sync hooks). Consider using a struct, implementing custom serialization and marking the class with [WeaverSafeClass], or decorating this SyncVar/SyncObject with [WeaverSafeClass] to ignore.", + "Usage", + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: "Class types used as SyncVars or SyncObjects should be value types or marked with [WeaverSafeClass] to avoid allocations and hook tracking issues.", + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1001"); + + public static readonly DiagnosticDescriptor AutoPropertyRule = new DiagnosticDescriptor( + AutoPropertyDiagnosticId, + "SyncVar property must be an auto-property", + "SyncVar property '{0}' must be a non-static auto-property with both get and set accessors", + "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Properties marked with [SyncVar] must be automatic properties with both getter and setter, and cannot be static.", + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1002"); + + public static readonly DiagnosticDescriptor DirectMutationRule = new DiagnosticDescriptor( + DirectMutationDiagnosticId, + "Direct mutation of elements inside SyncList/SyncDictionary", + "Direct mutation of elements inside '{0}' is not supported because changes cannot be tracked. Use SetItemDirty or modify the collection directly.", + "Usage", + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: "Direct mutation of elements inside SyncList or SyncDictionary won't trigger sync updates. Use SetItemDirty or modify the collection directly.", + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1003"); + + public static readonly DiagnosticDescriptor ReassignmentRule = new DiagnosticDescriptor( + ReassignmentDiagnosticId, + "Reassignment of ISyncObject fields", + "ISyncObject field '{0}' must be marked readonly and cannot be reassigned", + "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Fields implementing ISyncObject must be marked readonly and cannot be reassigned after initialization.", + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1004"); + + public static readonly DiagnosticDescriptor NetworkBehaviourAttributeRule = new DiagnosticDescriptor( + NetworkBehaviourAttributeDiagnosticId, + "Network attributes can only be used on NetworkBehaviour classes", + "Attribute '{0}' cannot be used on '{1}' because its declaring class does not inherit from NetworkBehaviour", + "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Network attributes like SyncVar, Server, Client, HasAuthority, LocalPlayer, ServerRpc, ClientRpc, and NetworkMethod are only valid inside NetworkBehaviour classes.", + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1101"); + + public static readonly DiagnosticDescriptor RpcSignatureRule = new DiagnosticDescriptor( + RpcSignatureDiagnosticId, + "RPC method must be non-generic and return void or UniTask", + "RPC method '{0}' is invalid: {1}", + "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Methods marked with ServerRpc or ClientRpc cannot be generic and must return void or UniTask.", + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1201"); + + public static readonly DiagnosticDescriptor RpcRefOutRule = new DiagnosticDescriptor( + RpcRefOutDiagnosticId, + "Pass-by-Reference Modifiers in RPCs", + "RPC method '{0}' cannot have ref or out parameter modifiers: parameter '{1}' is ref or out", + "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "RPC parameters cannot be pass-by-reference (ref/out).", + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1202"); + + public static readonly DiagnosticDescriptor RpcStaticRule = new DiagnosticDescriptor( + RpcStaticDiagnosticId, + "Static RPC Methods", + "RPC method '{0}' cannot be static", + "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "RPC methods cannot be static.", + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1203"); + + public static readonly DiagnosticDescriptor ClientRpcTargetRule = new DiagnosticDescriptor( + ClientRpcTargetDiagnosticId, + "Invalid ClientRpc Target Configurations", + "ClientRpc method '{0}' target configuration is invalid: {1}", + "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "ClientRpc target configurations must be valid.", + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1204"); + + public static readonly DiagnosticDescriptor RateLimitSettingsRule = new DiagnosticDescriptor( + RateLimitSettingsDiagnosticId, + "Invalid RateLimit Attribute Settings", + "RateLimit attribute on '{0}' has invalid settings: {1}", + "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "RateLimit parameters must be positive and MaxTokens >= Refill.", + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1205"); + + public static readonly DiagnosticDescriptor ServerRpcMissingRateLimitRule = new DiagnosticDescriptor( + ServerRpcMissingRateLimitDiagnosticId, + "Missing RateLimit on ServerRpc", + "ServerRpc method '{0}' should have a [RateLimit] attribute to prevent spam", + "Usage", + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: "Every ServerRpc should be protected by a [RateLimit] attribute.", + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1206"); + + public static readonly DiagnosticDescriptor MessageOrRpcRule = new DiagnosticDescriptor( + MessageOrRpcDiagnosticId, + "Class type used in NetworkMessage or RPC without WeaverSafeClass attribute", + "{0} '{1}' is a class type '{2}'. Class-based types allocate memory upon deserialization and do not support polymorphism (only declared fields serialize). Consider using a struct, implementing custom serialization and marking the class with [WeaverSafeClass], or decorating this member/parameter with [WeaverSafeClass] to ignore.", + "Usage", + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: "Class types used as NetworkMessage fields or RPC parameters/returns should be value types or marked with [WeaverSafeClass] to avoid allocations and polymorphism bugs.", + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1301"); + + public static readonly DiagnosticDescriptor FieldTypeSerializationRule = new DiagnosticDescriptor( + FieldTypeSerializationDiagnosticId, + "Field Type Serialization Validation", + "Type '{0}' used in '{1}' is not serializable by Mirage", + "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "All fields in NetworkMessages and parameters in RPCs must be serializable by Mirage.", + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1302"); + + public static readonly DiagnosticDescriptor MismatchedSerializationRule = new DiagnosticDescriptor( + MismatchedSerializationDiagnosticId, + "Mismatched Custom Serialization Methods", + "Custom serialization for type '{0}' is invalid: {1}", + "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Custom writer and reader extension methods must have matching signatures.", + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1303"); + + public static readonly DiagnosticDescriptor LifecycleNetworkStateRule = new DiagnosticDescriptor( + LifecycleNetworkStateDiagnosticId, + "Accessing Network State in Awake/Start", + "Network state member or SyncVar '{0}' should not be accessed in {1}", + "Usage", + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: "Network states are not yet initialized during Awake or Start.", + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1401"); + + public static readonly DiagnosticDescriptor LifecycleMissingBaseCallRule = new DiagnosticDescriptor( + LifecycleMissingBaseCallDiagnosticId, + "Missing base Call in OnSerialize/OnDeserialize", + "Overridden method '{0}' is missing a call to its base implementation", + "Usage", + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: "Overriding OnSerialize or OnDeserialize in a class that inherits from a class with synchronized state must call the base method.", + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1402"); + + public static readonly DiagnosticDescriptor PerformanceMtuExceededRule = new DiagnosticDescriptor( + PerformanceMtuExceededDiagnosticId, + "Network Message Exceeds Safe MTU", + "NetworkMessage '{0}' has an estimated serialized size of {1} bytes, which exceeds the safe MTU of {2} bytes", + "Performance", + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: "Network messages should remain within the safe MTU to avoid fragmentation.", + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1501"); + + public static readonly DiagnosticDescriptor PerformanceUnboundedCollectionRule = new DiagnosticDescriptor( + PerformanceUnboundedCollectionDiagnosticId, + "Unbounded String or Collection", + "Field/property/parameter '{0}' of type '{1}' is unbounded. Restrict its size using [BitCount] or another packing attribute.", + "Performance", + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: "Unbounded strings or collections can be exploited to cause memory exhaustion.", + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1502"); + + public static readonly DiagnosticDescriptor PerformanceHighOverheadRule = new DiagnosticDescriptor( + PerformanceHighOverheadDiagnosticId, + "High Bit-Overhead Primitive Type", + "Field/property/parameter '{0}' of type '{1}' is uncompressed. Consider applying a bit-packing or compression attribute.", + "Performance", + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: "Uncompressed primitives or vectors consume unnecessary bandwidth.", + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1503"); + + public static readonly ImmutableArray SupportedDiagnostics = ImmutableArray.Create( + SyncVarRule, + AutoPropertyRule, + DirectMutationRule, + ReassignmentRule, + NetworkBehaviourAttributeRule, + RpcSignatureRule, + RpcRefOutRule, + RpcStaticRule, + ClientRpcTargetRule, + RateLimitSettingsRule, + ServerRpcMissingRateLimitRule, + MessageOrRpcRule, + FieldTypeSerializationRule, + MismatchedSerializationRule, + LifecycleNetworkStateRule, + LifecycleMissingBaseCallRule, + PerformanceMtuExceededRule, + PerformanceUnboundedCollectionRule, + PerformanceHighOverheadRule + ); + } +} diff --git a/Mirage.Analyzers/MirageSymbols.cs b/Mirage.Analyzers/MirageSymbols.cs new file mode 100644 index 00000000000..cf3963d603b --- /dev/null +++ b/Mirage.Analyzers/MirageSymbols.cs @@ -0,0 +1,249 @@ +using Microsoft.CodeAnalysis; + +namespace Mirage.Analyzers +{ + public static class MirageAttributes + { + public class AttributeInfo + { + public string FullName { get; } + public string ShortName { get; } + + public AttributeInfo(string fullyQualifiedName) + { + FullName = fullyQualifiedName; + var lastDot = fullyQualifiedName.LastIndexOf('.'); + ShortName = lastDot >= 0 ? fullyQualifiedName.Substring(lastDot + 1) : fullyQualifiedName; + } + + public bool Has(ISymbol symbol) + { + if (symbol == null) + return false; + + foreach (var attr in symbol.GetAttributes()) + if (attr.AttributeClass != null && attr.AttributeClass.Name == ShortName && attr.AttributeClass.ToDisplayString() == FullName) + return true; + + return false; + } + + public bool TryGet(ISymbol symbol, out AttributeData attributeData) + { + attributeData = null!; + if (symbol == null) + return false; + + foreach (var attr in symbol.GetAttributes()) + if (attr.AttributeClass != null && attr.AttributeClass.Name == ShortName && attr.AttributeClass.ToDisplayString() == FullName) + { + attributeData = attr; + return true; + } + + return false; + } + + public bool TryGetNamedArgument(AttributeData attributeData, string name, out T value) + { + value = default!; + if (attributeData == null) + return false; + + foreach (var arg in attributeData.NamedArguments) + if (arg.Key == name) + { + if (arg.Value.Value is T val) + { + value = val; + return true; + } + return false; + } + + return false; + } + + public bool Matches(INamedTypeSymbol? attributeClass) + { + if (attributeClass == null) + return false; + + return attributeClass.Name == ShortName && attributeClass.ToDisplayString() == FullName; + } + } + + public static readonly AttributeInfo SyncVar = new AttributeInfo("Mirage.SyncVarAttribute"); + public static readonly AttributeInfo Server = new AttributeInfo("Mirage.ServerAttribute"); + public static readonly AttributeInfo Client = new AttributeInfo("Mirage.ClientAttribute"); + public static readonly AttributeInfo HasAuthority = new AttributeInfo("Mirage.HasAuthorityAttribute"); + public static readonly AttributeInfo LocalPlayer = new AttributeInfo("Mirage.LocalPlayerAttribute"); + public static readonly AttributeInfo ServerRpc = new AttributeInfo("Mirage.ServerRpcAttribute"); + public static readonly AttributeInfo ClientRpc = new AttributeInfo("Mirage.ClientRpcAttribute"); + public static readonly AttributeInfo NetworkMethod = new AttributeInfo("Mirage.NetworkMethodAttribute"); + public static readonly AttributeInfo WeaverSafeClass = new AttributeInfo("Mirage.WeaverSafeClassAttribute"); + public static readonly AttributeInfo NetworkMessage = new AttributeInfo("Mirage.NetworkMessageAttribute"); + public static readonly AttributeInfo BitCount = new AttributeInfo("Mirage.Serialization.BitCountAttribute"); + public static readonly AttributeInfo BitCountFromRange = new AttributeInfo("Mirage.Serialization.BitCountFromRangeAttribute"); + public static readonly AttributeInfo VarInt = new AttributeInfo("Mirage.Serialization.VarIntAttribute"); + public static readonly AttributeInfo VarIntBlocks = new AttributeInfo("Mirage.Serialization.VarIntBlocksAttribute"); + public static readonly AttributeInfo FloatPack = new AttributeInfo("Mirage.Serialization.FloatPackAttribute"); + public static readonly AttributeInfo Vector2Pack = new AttributeInfo("Mirage.Serialization.Vector2PackAttribute"); + public static readonly AttributeInfo Vector3Pack = new AttributeInfo("Mirage.Serialization.Vector3PackAttribute"); + public static readonly AttributeInfo QuaternionPack = new AttributeInfo("Mirage.Serialization.QuaternionPackAttribute"); + public static readonly AttributeInfo RateLimit = new AttributeInfo("Mirage.RateLimitAttribute"); + + public static readonly AttributeInfo[] NetworkAttributes = new[] + { + SyncVar, + Server, + Client, + HasAuthority, + LocalPlayer, + ServerRpc, + ClientRpc, + NetworkMethod + }; + + public static bool HasCompressionAttribute(ISymbol symbol, ITypeSymbol type) + { + if (symbol == null || type == null) + return false; + + if (type.SpecialType == SpecialType.System_Int32 || + type.SpecialType == SpecialType.System_UInt32 || + type.SpecialType == SpecialType.System_Int64 || + type.SpecialType == SpecialType.System_UInt64 || + type.SpecialType == SpecialType.System_String || + type.TypeKind == TypeKind.Array || + MirageTypes.IEnumerable.Implements(type)) + return BitCount.Has(symbol) || + BitCountFromRange.Has(symbol) || + VarInt.Has(symbol) || + VarIntBlocks.Has(symbol); + + if (type.SpecialType == SpecialType.System_Single || + type.SpecialType == SpecialType.System_Double) + return FloatPack.Has(symbol); + + if (MirageTypes.Vector2.Is(type)) + return Vector2Pack.Has(symbol); + + if (MirageTypes.Vector3.Is(type)) + return Vector3Pack.Has(symbol); + + if (MirageTypes.Quaternion.Is(type)) + return QuaternionPack.Has(symbol); + + return false; + } + } + + public static class MirageTypes + { + public class TypeInfo + { + public string FullName { get; } + public string ShortName { get; } + + public TypeInfo(string fullyQualifiedName) + { + FullName = fullyQualifiedName; + var lastDot = fullyQualifiedName.LastIndexOf('.'); + ShortName = lastDot >= 0 ? fullyQualifiedName.Substring(lastDot + 1) : fullyQualifiedName; + } + + public bool Is(ITypeSymbol typeSymbol) + { + if (typeSymbol == null) + return false; + + var displayString = typeSymbol.OriginalDefinition.ToDisplayString(); + var index = displayString.IndexOf('<'); + if (index >= 0) + displayString = displayString.Substring(0, index); + + return typeSymbol.Name == ShortName && displayString == FullName; + } + + public bool IsOrInherits(ITypeSymbol typeSymbol) + { + if (typeSymbol == null) + return false; + + var current = typeSymbol; + while (current != null) + { + if (Is(current)) + return true; + + current = current.BaseType; + } + return false; + } + + public bool Implements(ITypeSymbol typeSymbol) + { + if (typeSymbol == null) + return false; + + if (typeSymbol is INamedTypeSymbol namedType && namedType.TypeKind == TypeKind.Interface && Is(namedType)) + return true; + + foreach (var iface in typeSymbol.AllInterfaces) + if (Is(iface)) + return true; + + return false; + } + } + + public static readonly TypeInfo NetworkBehaviour = new TypeInfo("Mirage.NetworkBehaviour"); + public static readonly TypeInfo GameObject = new TypeInfo("UnityEngine.GameObject"); + public static readonly TypeInfo NetworkIdentity = new TypeInfo("Mirage.NetworkIdentity"); + public static readonly TypeInfo ISyncObject = new TypeInfo("Mirage.Collections.ISyncObject"); + public static readonly TypeInfo NetworkWriter = new TypeInfo("Mirage.Serialization.NetworkWriter"); + public static readonly TypeInfo NetworkReader = new TypeInfo("Mirage.Serialization.NetworkReader"); + public static readonly TypeInfo INetworkPlayer = new TypeInfo("Mirage.INetworkPlayer"); + public static readonly TypeInfo NetworkPlayer = new TypeInfo("Mirage.NetworkPlayer"); + public static readonly TypeInfo NetworkConnection = new TypeInfo("Mirage.NetworkConnection"); + public static readonly TypeInfo IEnumerable = new TypeInfo("System.Collections.IEnumerable"); + public static readonly TypeInfo UniTask = new TypeInfo("Cysharp.Threading.Tasks.UniTask"); + public static readonly TypeInfo SyncList = new TypeInfo("Mirage.Collections.SyncList"); + public static readonly TypeInfo SyncDictionary = new TypeInfo("Mirage.Collections.SyncDictionary"); + public static readonly TypeInfo SyncIDictionary = new TypeInfo("Mirage.Collections.SyncIDictionary"); + public static readonly TypeInfo Vector2 = new TypeInfo("UnityEngine.Vector2"); + public static readonly TypeInfo Vector3 = new TypeInfo("UnityEngine.Vector3"); + public static readonly TypeInfo Quaternion = new TypeInfo("UnityEngine.Quaternion"); + + public static bool HasSynchronizedState(INamedTypeSymbol typeSymbol) + { + if (typeSymbol == null) + return false; + + foreach (var member in typeSymbol.GetMembers()) + { + if (member is IFieldSymbol field) + if (MirageAttributes.SyncVar.Has(field) || MirageTypes.ISyncObject.Implements(field.Type)) + return true; + + if (member is IPropertySymbol prop) + if (MirageAttributes.SyncVar.Has(prop) || MirageTypes.ISyncObject.Implements(prop.Type)) + return true; + } + + return false; + } + + public static bool IsNetworkPlayerOrConnection(ITypeSymbol typeSymbol) + { + if (typeSymbol == null) + return false; + + return INetworkPlayer.IsOrInherits(typeSymbol) || + NetworkPlayer.IsOrInherits(typeSymbol) || + NetworkConnection.IsOrInherits(typeSymbol) || + INetworkPlayer.Implements(typeSymbol); + } + } +} diff --git a/Mirage.Analyzers/NetworkBehaviourAttributeAnalyzer.cs b/Mirage.Analyzers/NetworkBehaviourAttributeAnalyzer.cs deleted file mode 100644 index 18500e6fa17..00000000000 --- a/Mirage.Analyzers/NetworkBehaviourAttributeAnalyzer.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System.Collections.Immutable; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; - -namespace Mirage.Analyzers -{ - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public class NetworkBehaviourAttributeAnalyzer : DiagnosticAnalyzer - { - public const string DiagnosticId = "MIRAGE1101"; - - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( - DiagnosticId, - "Network attributes can only be used on NetworkBehaviour classes", - "Attribute '{0}' cannot be used on '{1}' because its declaring class does not inherit from NetworkBehaviour", - "Usage", - DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: "Network attributes like SyncVar, Server, Client, HasAuthority, LocalPlayer, ServerRpc, ClientRpc, and NetworkMethod are only valid inside NetworkBehaviour classes.", - helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1101"); - - private static readonly ImmutableHashSet NetworkAttributes = ImmutableHashSet.Create( - "Mirage.SyncVarAttribute", - "Mirage.ServerAttribute", - "Mirage.ClientAttribute", - "Mirage.HasAuthorityAttribute", - "Mirage.LocalPlayerAttribute", - "Mirage.ServerRpcAttribute", - "Mirage.ClientRpcAttribute", - "Mirage.NetworkMethodAttribute" - ); - - private static readonly ImmutableHashSet ShortNetworkAttributes = ImmutableHashSet.Create( - "SyncVarAttribute", - "ServerAttribute", - "ClientAttribute", - "HasAuthorityAttribute", - "LocalPlayerAttribute", - "ServerRpcAttribute", - "ClientRpcAttribute", - "NetworkMethodAttribute" - ); - - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); - - public override void Initialize(AnalysisContext context) - { - context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); - context.EnableConcurrentExecution(); - - context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Field, SymbolKind.Property, SymbolKind.Method); - } - - private static void AnalyzeSymbol(SymbolAnalysisContext context) - { - var symbol = context.Symbol; - - var attributes = symbol.GetAttributes(); - if (attributes.IsEmpty) - return; - - var hasNetworkAttribute = false; - foreach (var attr in attributes) - { - if (attr.AttributeClass != null && ShortNetworkAttributes.Contains(attr.AttributeClass.Name)) - { - var fullName = attr.AttributeClass.ToDisplayString(); - if (NetworkAttributes.Contains(fullName)) - { - hasNetworkAttribute = true; - break; - } - } - } - - if (!hasNetworkAttribute) - return; - - var containingType = symbol.ContainingType; - - // Stop analysis if the declaring type inherits from NetworkBehaviour, as network attributes are valid in this context. - if (containingType != null && IsOrInheritsFrom(containingType, "Mirage.NetworkBehaviour")) - return; - - foreach (var attr in attributes) - { - if (attr.AttributeClass != null && ShortNetworkAttributes.Contains(attr.AttributeClass.Name)) - { - var fullName = attr.AttributeClass.ToDisplayString(); - if (NetworkAttributes.Contains(fullName)) - { - var location = attr.ApplicationSyntaxReference?.GetSyntax()?.GetLocation() ?? symbol.Locations[0]; - var diagnostic = Diagnostic.Create(Rule, location, attr.AttributeClass.Name, symbol.Name); - context.ReportDiagnostic(diagnostic); - } - } - } - } - - private static bool IsOrInheritsFrom(ITypeSymbol typeSymbol, string fullyQualifiedName) - { - var lastDot = fullyQualifiedName.LastIndexOf('.'); - var shortName = lastDot >= 0 ? fullyQualifiedName.Substring(lastDot + 1) : fullyQualifiedName; - - var current = typeSymbol; - while (current != null) - { - if (current.Name == shortName && current.ToDisplayString() == fullyQualifiedName) - return true; - current = current.BaseType; - } - return false; - } - } -} diff --git a/Mirage.Analyzers/RpcSignatureAnalyzer.cs b/Mirage.Analyzers/RpcSignatureAnalyzer.cs deleted file mode 100644 index 5143188d5f0..00000000000 --- a/Mirage.Analyzers/RpcSignatureAnalyzer.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System.Collections.Immutable; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; - -namespace Mirage.Analyzers -{ - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public class RpcSignatureAnalyzer : DiagnosticAnalyzer - { - public const string DiagnosticId = "MIRAGE1201"; - - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( - DiagnosticId, - "RPC method must be non-generic and return void or UniTask", - "RPC method '{0}' is invalid: {1}", - "Usage", - DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: "Methods marked with ServerRpc or ClientRpc cannot be generic and must return void or UniTask.", - helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1201"); - - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); - - public override void Initialize(AnalysisContext context) - { - context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); - context.EnableConcurrentExecution(); - - context.RegisterSymbolAction(AnalyzeMethod, SymbolKind.Method); - } - - private static void AnalyzeMethod(SymbolAnalysisContext context) - { - var methodSymbol = (IMethodSymbol)context.Symbol; - - // Stop analysis if the method is not a ServerRpc or ClientRpc, as RPC rules only apply to them. - if (!IsRpcMethod(methodSymbol)) - return; - - if (methodSymbol.IsGenericMethod) - { - var diagnostic = Diagnostic.Create(Rule, methodSymbol.Locations[0], methodSymbol.Name, "cannot have generic parameters"); - context.ReportDiagnostic(diagnostic); - } - - if (!IsVoidOrUniTask(methodSymbol.ReturnType)) - { - var diagnostic = Diagnostic.Create(Rule, methodSymbol.Locations[0], methodSymbol.Name, $"cannot return '{methodSymbol.ReturnType.ToDisplayString()}' (must return void or UniTask)"); - context.ReportDiagnostic(diagnostic); - } - } - - private static bool IsRpcMethod(IMethodSymbol methodSymbol) - { - foreach (var attr in methodSymbol.GetAttributes()) - { - if (attr.AttributeClass != null && (attr.AttributeClass.Name == "ServerRpcAttribute" || attr.AttributeClass.Name == "ClientRpcAttribute")) - { - var fullName = attr.AttributeClass.ToDisplayString(); - if (fullName == "Mirage.ServerRpcAttribute" || fullName == "Mirage.ClientRpcAttribute") - return true; - } - } - return false; - } - - private static bool IsVoidOrUniTask(ITypeSymbol typeSymbol) - { - if (typeSymbol == null) - return false; - - if (typeSymbol.SpecialType == SpecialType.System_Void) - return true; - - var originalDefinition = typeSymbol.OriginalDefinition; - if (originalDefinition == null) - return false; - - if (originalDefinition.Name == "UniTask") - { - var originalString = originalDefinition.ToDisplayString(); - if (originalString == "Cysharp.Threading.Tasks.UniTask" || originalString.StartsWith("Cysharp.Threading.Tasks.UniTask<")) - return true; - } - - return false; - } - } -} diff --git a/Mirage.Analyzers/WeaverSafeClassAnalyzer.cs b/Mirage.Analyzers/WeaverSafeClassAnalyzer.cs deleted file mode 100644 index b0105f9c5b4..00000000000 --- a/Mirage.Analyzers/WeaverSafeClassAnalyzer.cs +++ /dev/null @@ -1,255 +0,0 @@ -using System.Collections.Immutable; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; - -namespace Mirage.Analyzers -{ - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public class WeaverSafeClassAnalyzer : DiagnosticAnalyzer - { - public const string SyncVarDiagnosticId = "MIRAGE1001"; - public const string AutoPropertyDiagnosticId = "MIRAGE1002"; - public const string MessageOrRpcDiagnosticId = "MIRAGE1301"; - - private static readonly DiagnosticDescriptor SyncVarRule = new DiagnosticDescriptor( - SyncVarDiagnosticId, - "SyncVar cannot be a class type unless marked safe", - "SyncVar '{0}' is a class type '{1}'. Class-based SyncVars allocate memory, do not support polymorphism (only declared fields serialize), and cannot track internal changes automatically (meaning modifications won't trigger sync hooks). Consider using a struct, implementing custom serialization and marking the class with [WeaverSafeClass], or decorating this SyncVar with [WeaverSafeClass] to ignore.", - "Usage", - DiagnosticSeverity.Warning, - isEnabledByDefault: true, - description: "Class types used as SyncVars should be value types or marked with [WeaverSafeClass] to avoid allocations and hook tracking issues.", - helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1001"); - - private static readonly DiagnosticDescriptor AutoPropertyRule = new DiagnosticDescriptor( - AutoPropertyDiagnosticId, - "SyncVar property must be an auto-property", - "SyncVar property '{0}' must be a non-static auto-property with both get and set accessors", - "Usage", - DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: "Properties marked with [SyncVar] must be automatic properties with both getter and setter, and cannot be static.", - helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1002"); - - private static readonly DiagnosticDescriptor MessageOrRpcRule = new DiagnosticDescriptor( - MessageOrRpcDiagnosticId, - "Class type used in NetworkMessage or RPC without WeaverSafeClass attribute", - "{0} '{1}' is a class type '{2}'. Class-based types allocate memory upon deserialization and do not support polymorphism (only declared fields serialize). Consider using a struct, implementing custom serialization and marking the class with [WeaverSafeClass], or decorating this member/parameter with [WeaverSafeClass] to ignore.", - "Usage", - DiagnosticSeverity.Warning, - isEnabledByDefault: true, - description: "Class types used as NetworkMessage fields or RPC parameters/returns should be value types or marked with [WeaverSafeClass] to avoid allocations and polymorphism bugs.", - helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1301"); - - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(SyncVarRule, AutoPropertyRule, MessageOrRpcRule); - - public override void Initialize(AnalysisContext context) - { - context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); - context.EnableConcurrentExecution(); - - context.RegisterSymbolAction(AnalyzeField, SymbolKind.Field); - context.RegisterSymbolAction(AnalyzeProperty, SymbolKind.Property); - context.RegisterSymbolAction(AnalyzeParameter, SymbolKind.Parameter); - context.RegisterSymbolAction(AnalyzeMethod, SymbolKind.Method); - } - - private static void AnalyzeField(SymbolAnalysisContext context) - { - var fieldSymbol = (IFieldSymbol)context.Symbol; - - if (HasAttribute(fieldSymbol, "Mirage.SyncVarAttribute")) - { - AnalyzeSyncVar(context, fieldSymbol, fieldSymbol.Type); - return; - } - - if (fieldSymbol.ContainingType != null && HasAttribute(fieldSymbol.ContainingType, "Mirage.NetworkMessageAttribute")) - { - AnalyzeMessageOrRpc(context, fieldSymbol, fieldSymbol.Type, "NetworkMessage field"); - } - } - - private static void AnalyzeProperty(SymbolAnalysisContext context) - { - var propertySymbol = (IPropertySymbol)context.Symbol; - - if (HasAttribute(propertySymbol, "Mirage.SyncVarAttribute")) - { - if (propertySymbol.GetMethod == null || propertySymbol.SetMethod == null || propertySymbol.IsStatic || !IsAutoProperty(propertySymbol)) - { - var diagnostic = Diagnostic.Create(AutoPropertyRule, propertySymbol.Locations[0], propertySymbol.Name); - context.ReportDiagnostic(diagnostic); - return; - } - - AnalyzeSyncVar(context, propertySymbol, propertySymbol.Type); - return; - } - - if (propertySymbol.ContainingType != null && HasAttribute(propertySymbol.ContainingType, "Mirage.NetworkMessageAttribute")) - { - AnalyzeMessageOrRpc(context, propertySymbol, propertySymbol.Type, "NetworkMessage property"); - } - } - - private static void AnalyzeParameter(SymbolAnalysisContext context) - { - var parameterSymbol = (IParameterSymbol)context.Symbol; - var containingMethod = parameterSymbol.ContainingSymbol as IMethodSymbol; - - if (containingMethod == null) - return; - - if (IsRpcMethod(containingMethod)) - { - AnalyzeMessageOrRpc(context, parameterSymbol, parameterSymbol.Type, "RPC parameter"); - } - } - - private static void AnalyzeMethod(SymbolAnalysisContext context) - { - var methodSymbol = (IMethodSymbol)context.Symbol; - - if (IsRpcMethod(methodSymbol)) - { - var returnType = methodSymbol.ReturnType as INamedTypeSymbol; - if (returnType != null && returnType.IsGenericType && returnType.TypeArguments.Length > 0) - { - var originalDefinition = returnType.OriginalDefinition; - if (originalDefinition != null && originalDefinition.Name == "UniTask") - { - var originalString = originalDefinition.ToDisplayString(); - if (originalString == "Cysharp.Threading.Tasks.UniTask") - { - var typeArgument = returnType.TypeArguments[0]; - AnalyzeMessageOrRpc(context, methodSymbol, typeArgument, "RPC return type"); - } - } - } - } - } - - private static bool IsBasicSafeType(ITypeSymbol typeSymbol) - { - if (typeSymbol == null) - return true; - - if (typeSymbol.IsValueType || typeSymbol.TypeKind == TypeKind.Struct || typeSymbol.TypeKind == TypeKind.Enum) - return true; - - if (typeSymbol.TypeKind != TypeKind.Class) - return true; - - if (typeSymbol.SpecialType == SpecialType.System_String) - return true; - - if (IsOrInheritsFrom(typeSymbol, "Mirage.NetworkIdentity")) - return true; - if (IsOrInheritsFrom(typeSymbol, "UnityEngine.GameObject")) - return true; - if (IsOrInheritsFrom(typeSymbol, "Mirage.NetworkBehaviour")) - return true; - - return false; - } - - private static bool IsExplicitlyMarkedSafe(ISymbol symbol, ITypeSymbol typeSymbol) - { - if (HasAttribute(symbol, "Mirage.WeaverSafeClassAttribute")) - return true; - - if (typeSymbol != null && HasAttribute(typeSymbol, "Mirage.WeaverSafeClassAttribute")) - return true; - - if (symbol.ContainingType != null && HasAttribute(symbol.ContainingType, "Mirage.WeaverSafeClassAttribute")) - return true; - - return false; - } - - private static void AnalyzeSyncVar(SymbolAnalysisContext context, ISymbol symbol, ITypeSymbol typeSymbol) - { - if (IsBasicSafeType(typeSymbol)) - return; - - if (IsExplicitlyMarkedSafe(symbol, typeSymbol)) - return; - - var diagnostic = Diagnostic.Create(SyncVarRule, symbol.Locations[0], symbol.Name, typeSymbol.Name); - context.ReportDiagnostic(diagnostic); - } - - private static void AnalyzeMessageOrRpc(SymbolAnalysisContext context, ISymbol symbol, ITypeSymbol typeSymbol, string contextName) - { - if (IsBasicSafeType(typeSymbol)) - return; - - if (IsExplicitlyMarkedSafe(symbol, typeSymbol)) - return; - - var ns = typeSymbol.ContainingNamespace?.ToDisplayString(); - if (ns != null && (ns == "System.Collections.Generic" || ns == "System.Collections")) - return; - - var diagnostic = Diagnostic.Create(MessageOrRpcRule, symbol.Locations[0], contextName, symbol.Name, typeSymbol.Name); - context.ReportDiagnostic(diagnostic); - } - - private static bool IsRpcMethod(IMethodSymbol methodSymbol) - { - foreach (var attr in methodSymbol.GetAttributes()) - { - if (attr.AttributeClass != null && (attr.AttributeClass.Name == "ServerRpcAttribute" || attr.AttributeClass.Name == "ClientRpcAttribute")) - { - var fullName = attr.AttributeClass.ToDisplayString(); - if (fullName == "Mirage.ServerRpcAttribute" || fullName == "Mirage.ClientRpcAttribute") - return true; - } - } - return false; - } - - private static bool HasAttribute(ISymbol symbol, string fullyQualifiedName) - { - var lastDot = fullyQualifiedName.LastIndexOf('.'); - var shortName = lastDot >= 0 ? fullyQualifiedName.Substring(lastDot + 1) : fullyQualifiedName; - - foreach (var attr in symbol.GetAttributes()) - { - if (attr.AttributeClass != null && attr.AttributeClass.Name == shortName) - { - if (attr.AttributeClass.ToDisplayString() == fullyQualifiedName) - return true; - } - } - return false; - } - - private static bool IsOrInheritsFrom(ITypeSymbol typeSymbol, string fullyQualifiedName) - { - var lastDot = fullyQualifiedName.LastIndexOf('.'); - var shortName = lastDot >= 0 ? fullyQualifiedName.Substring(lastDot + 1) : fullyQualifiedName; - - var current = typeSymbol; - while (current != null) - { - if (current.Name == shortName && current.ToDisplayString() == fullyQualifiedName) - return true; - current = current.BaseType; - } - return false; - } - - private static bool IsAutoProperty(IPropertySymbol propertySymbol) - { - var backingFieldName = $"<{propertySymbol.Name}>k__BackingField"; - foreach (var member in propertySymbol.ContainingType.GetMembers()) - { - if (member is IFieldSymbol field && field.IsImplicitlyDeclared && field.Name == backingFieldName) - return true; - } - return false; - } - } -} From f1be60f01e98c445107c7e5dc50b5fb0d5070ad7 Mon Sep 17 00:00:00 2001 From: James Frowen Date: Sun, 31 May 2026 17:56:12 +0100 Subject: [PATCH 20/41] refactor: move c# Analyzers folders --- .../.gitignore | 0 .../AwakeStartNetworkStateTests.cs | 0 .../DirectMutationTests.cs | 0 .../FieldSerializationTests.cs | 0 .../Mirage.Analyzers.Tests.csproj | 20 +++++++++++++++++++ .../MismatchedSerializerTests.cs | 0 .../MtuSizeEstimationTests.cs | 0 .../OnSerializeBaseCallTests.cs | 0 .../RateLimitSettingsTests.cs | 0 .../RpcClientTargetTests.cs | 0 .../RpcPassByRefTests.cs | 0 .../Mirage.Analyzers.Tests}/RpcStaticTests.cs | 0 .../ServerRpcRateLimitMissingTests.cs | 0 .../SyncObjectReassignmentTests.cs | 0 .../SyncVarClassTests.cs | 0 .../UnboundedCollectionTests.cs | 0 .../UncompressedPrimitiveTests.cs | 0 .../Mirage.Analyzers.Tests}/VerifyCS.cs | 0 .../Mirage.Analyzers.sln | 4 ++-- .../Mirage.Analyzers}/Mirage.Analyzers.csproj | 2 +- .../MirageAnalyzer.Lifecycles.cs | 0 .../MirageAnalyzer.Performance.cs | 0 .../Mirage.Analyzers}/MirageAnalyzer.Rpcs.cs | 0 .../MirageAnalyzer.Serialization.cs | 0 .../MirageAnalyzer.SyncVars.cs | 0 .../Mirage.Analyzers}/MirageAnalyzer.cs | 0 .../Mirage.Analyzers}/MirageRules.cs | 0 .../Mirage.Analyzers}/MirageSymbols.cs | 0 28 files changed, 23 insertions(+), 3 deletions(-) rename {Mirage.Analyzers => CSharpAnalyzers}/.gitignore (100%) rename {Mirage.Analyzers.Tests => CSharpAnalyzers/Mirage.Analyzers.Tests}/AwakeStartNetworkStateTests.cs (100%) rename {Mirage.Analyzers.Tests => CSharpAnalyzers/Mirage.Analyzers.Tests}/DirectMutationTests.cs (100%) rename {Mirage.Analyzers.Tests => CSharpAnalyzers/Mirage.Analyzers.Tests}/FieldSerializationTests.cs (100%) create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/Mirage.Analyzers.Tests.csproj rename {Mirage.Analyzers.Tests => CSharpAnalyzers/Mirage.Analyzers.Tests}/MismatchedSerializerTests.cs (100%) rename {Mirage.Analyzers.Tests => CSharpAnalyzers/Mirage.Analyzers.Tests}/MtuSizeEstimationTests.cs (100%) rename {Mirage.Analyzers.Tests => CSharpAnalyzers/Mirage.Analyzers.Tests}/OnSerializeBaseCallTests.cs (100%) rename {Mirage.Analyzers.Tests => CSharpAnalyzers/Mirage.Analyzers.Tests}/RateLimitSettingsTests.cs (100%) rename {Mirage.Analyzers.Tests => CSharpAnalyzers/Mirage.Analyzers.Tests}/RpcClientTargetTests.cs (100%) rename {Mirage.Analyzers.Tests => CSharpAnalyzers/Mirage.Analyzers.Tests}/RpcPassByRefTests.cs (100%) rename {Mirage.Analyzers.Tests => CSharpAnalyzers/Mirage.Analyzers.Tests}/RpcStaticTests.cs (100%) rename {Mirage.Analyzers.Tests => CSharpAnalyzers/Mirage.Analyzers.Tests}/ServerRpcRateLimitMissingTests.cs (100%) rename {Mirage.Analyzers.Tests => CSharpAnalyzers/Mirage.Analyzers.Tests}/SyncObjectReassignmentTests.cs (100%) rename {Mirage.Analyzers.Tests => CSharpAnalyzers/Mirage.Analyzers.Tests}/SyncVarClassTests.cs (100%) rename {Mirage.Analyzers.Tests => CSharpAnalyzers/Mirage.Analyzers.Tests}/UnboundedCollectionTests.cs (100%) rename {Mirage.Analyzers.Tests => CSharpAnalyzers/Mirage.Analyzers.Tests}/UncompressedPrimitiveTests.cs (100%) rename {Mirage.Analyzers.Tests => CSharpAnalyzers/Mirage.Analyzers.Tests}/VerifyCS.cs (100%) rename {Mirage.Analyzers => CSharpAnalyzers}/Mirage.Analyzers.sln (87%) rename {Mirage.Analyzers => CSharpAnalyzers/Mirage.Analyzers}/Mirage.Analyzers.csproj (89%) rename {Mirage.Analyzers => CSharpAnalyzers/Mirage.Analyzers}/MirageAnalyzer.Lifecycles.cs (100%) rename {Mirage.Analyzers => CSharpAnalyzers/Mirage.Analyzers}/MirageAnalyzer.Performance.cs (100%) rename {Mirage.Analyzers => CSharpAnalyzers/Mirage.Analyzers}/MirageAnalyzer.Rpcs.cs (100%) rename {Mirage.Analyzers => CSharpAnalyzers/Mirage.Analyzers}/MirageAnalyzer.Serialization.cs (100%) rename {Mirage.Analyzers => CSharpAnalyzers/Mirage.Analyzers}/MirageAnalyzer.SyncVars.cs (100%) rename {Mirage.Analyzers => CSharpAnalyzers/Mirage.Analyzers}/MirageAnalyzer.cs (100%) rename {Mirage.Analyzers => CSharpAnalyzers/Mirage.Analyzers}/MirageRules.cs (100%) rename {Mirage.Analyzers => CSharpAnalyzers/Mirage.Analyzers}/MirageSymbols.cs (100%) diff --git a/Mirage.Analyzers/.gitignore b/CSharpAnalyzers/.gitignore similarity index 100% rename from Mirage.Analyzers/.gitignore rename to CSharpAnalyzers/.gitignore diff --git a/Mirage.Analyzers.Tests/AwakeStartNetworkStateTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/AwakeStartNetworkStateTests.cs similarity index 100% rename from Mirage.Analyzers.Tests/AwakeStartNetworkStateTests.cs rename to CSharpAnalyzers/Mirage.Analyzers.Tests/AwakeStartNetworkStateTests.cs diff --git a/Mirage.Analyzers.Tests/DirectMutationTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/DirectMutationTests.cs similarity index 100% rename from Mirage.Analyzers.Tests/DirectMutationTests.cs rename to CSharpAnalyzers/Mirage.Analyzers.Tests/DirectMutationTests.cs diff --git a/Mirage.Analyzers.Tests/FieldSerializationTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/FieldSerializationTests.cs similarity index 100% rename from Mirage.Analyzers.Tests/FieldSerializationTests.cs rename to CSharpAnalyzers/Mirage.Analyzers.Tests/FieldSerializationTests.cs diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/Mirage.Analyzers.Tests.csproj b/CSharpAnalyzers/Mirage.Analyzers.Tests/Mirage.Analyzers.Tests.csproj new file mode 100644 index 00000000000..682efa3cfab --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/Mirage.Analyzers.Tests.csproj @@ -0,0 +1,20 @@ + + + net6.0 + enable + false + + + + + + + + + + + + + + + diff --git a/Mirage.Analyzers.Tests/MismatchedSerializerTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/MismatchedSerializerTests.cs similarity index 100% rename from Mirage.Analyzers.Tests/MismatchedSerializerTests.cs rename to CSharpAnalyzers/Mirage.Analyzers.Tests/MismatchedSerializerTests.cs diff --git a/Mirage.Analyzers.Tests/MtuSizeEstimationTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/MtuSizeEstimationTests.cs similarity index 100% rename from Mirage.Analyzers.Tests/MtuSizeEstimationTests.cs rename to CSharpAnalyzers/Mirage.Analyzers.Tests/MtuSizeEstimationTests.cs diff --git a/Mirage.Analyzers.Tests/OnSerializeBaseCallTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/OnSerializeBaseCallTests.cs similarity index 100% rename from Mirage.Analyzers.Tests/OnSerializeBaseCallTests.cs rename to CSharpAnalyzers/Mirage.Analyzers.Tests/OnSerializeBaseCallTests.cs diff --git a/Mirage.Analyzers.Tests/RateLimitSettingsTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/RateLimitSettingsTests.cs similarity index 100% rename from Mirage.Analyzers.Tests/RateLimitSettingsTests.cs rename to CSharpAnalyzers/Mirage.Analyzers.Tests/RateLimitSettingsTests.cs diff --git a/Mirage.Analyzers.Tests/RpcClientTargetTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/RpcClientTargetTests.cs similarity index 100% rename from Mirage.Analyzers.Tests/RpcClientTargetTests.cs rename to CSharpAnalyzers/Mirage.Analyzers.Tests/RpcClientTargetTests.cs diff --git a/Mirage.Analyzers.Tests/RpcPassByRefTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/RpcPassByRefTests.cs similarity index 100% rename from Mirage.Analyzers.Tests/RpcPassByRefTests.cs rename to CSharpAnalyzers/Mirage.Analyzers.Tests/RpcPassByRefTests.cs diff --git a/Mirage.Analyzers.Tests/RpcStaticTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/RpcStaticTests.cs similarity index 100% rename from Mirage.Analyzers.Tests/RpcStaticTests.cs rename to CSharpAnalyzers/Mirage.Analyzers.Tests/RpcStaticTests.cs diff --git a/Mirage.Analyzers.Tests/ServerRpcRateLimitMissingTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/ServerRpcRateLimitMissingTests.cs similarity index 100% rename from Mirage.Analyzers.Tests/ServerRpcRateLimitMissingTests.cs rename to CSharpAnalyzers/Mirage.Analyzers.Tests/ServerRpcRateLimitMissingTests.cs diff --git a/Mirage.Analyzers.Tests/SyncObjectReassignmentTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/SyncObjectReassignmentTests.cs similarity index 100% rename from Mirage.Analyzers.Tests/SyncObjectReassignmentTests.cs rename to CSharpAnalyzers/Mirage.Analyzers.Tests/SyncObjectReassignmentTests.cs diff --git a/Mirage.Analyzers.Tests/SyncVarClassTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/SyncVarClassTests.cs similarity index 100% rename from Mirage.Analyzers.Tests/SyncVarClassTests.cs rename to CSharpAnalyzers/Mirage.Analyzers.Tests/SyncVarClassTests.cs diff --git a/Mirage.Analyzers.Tests/UnboundedCollectionTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/UnboundedCollectionTests.cs similarity index 100% rename from Mirage.Analyzers.Tests/UnboundedCollectionTests.cs rename to CSharpAnalyzers/Mirage.Analyzers.Tests/UnboundedCollectionTests.cs diff --git a/Mirage.Analyzers.Tests/UncompressedPrimitiveTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/UncompressedPrimitiveTests.cs similarity index 100% rename from Mirage.Analyzers.Tests/UncompressedPrimitiveTests.cs rename to CSharpAnalyzers/Mirage.Analyzers.Tests/UncompressedPrimitiveTests.cs diff --git a/Mirage.Analyzers.Tests/VerifyCS.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/VerifyCS.cs similarity index 100% rename from Mirage.Analyzers.Tests/VerifyCS.cs rename to CSharpAnalyzers/Mirage.Analyzers.Tests/VerifyCS.cs diff --git a/Mirage.Analyzers/Mirage.Analyzers.sln b/CSharpAnalyzers/Mirage.Analyzers.sln similarity index 87% rename from Mirage.Analyzers/Mirage.Analyzers.sln rename to CSharpAnalyzers/Mirage.Analyzers.sln index b7d89177dd8..e97fd7afa5d 100644 --- a/Mirage.Analyzers/Mirage.Analyzers.sln +++ b/CSharpAnalyzers/Mirage.Analyzers.sln @@ -3,9 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 18 VisualStudioVersion = 18.5.11716.220 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mirage.Analyzers", "Mirage.Analyzers.csproj", "{F8AD7409-6FB0-5775-D080-236CB9B32B00}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mirage.Analyzers", "Mirage.Analyzers\Mirage.Analyzers.csproj", "{F8AD7409-6FB0-5775-D080-236CB9B32B00}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mirage.Analyzers.Tests", "..\Mirage.Analyzers.Tests\Mirage.Analyzers.Tests.csproj", "{848E7273-08A6-A8DF-F745-3425A250804B}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mirage.Analyzers.Tests", "Mirage.Analyzers.Tests\Mirage.Analyzers.Tests.csproj", "{848E7273-08A6-A8DF-F745-3425A250804B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/Mirage.Analyzers/Mirage.Analyzers.csproj b/CSharpAnalyzers/Mirage.Analyzers/Mirage.Analyzers.csproj similarity index 89% rename from Mirage.Analyzers/Mirage.Analyzers.csproj rename to CSharpAnalyzers/Mirage.Analyzers/Mirage.Analyzers.csproj index e8bb018ed16..5a07600418a 100644 --- a/Mirage.Analyzers/Mirage.Analyzers.csproj +++ b/CSharpAnalyzers/Mirage.Analyzers/Mirage.Analyzers.csproj @@ -13,6 +13,6 @@ - + diff --git a/Mirage.Analyzers/MirageAnalyzer.Lifecycles.cs b/CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.Lifecycles.cs similarity index 100% rename from Mirage.Analyzers/MirageAnalyzer.Lifecycles.cs rename to CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.Lifecycles.cs diff --git a/Mirage.Analyzers/MirageAnalyzer.Performance.cs b/CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.Performance.cs similarity index 100% rename from Mirage.Analyzers/MirageAnalyzer.Performance.cs rename to CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.Performance.cs diff --git a/Mirage.Analyzers/MirageAnalyzer.Rpcs.cs b/CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.Rpcs.cs similarity index 100% rename from Mirage.Analyzers/MirageAnalyzer.Rpcs.cs rename to CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.Rpcs.cs diff --git a/Mirage.Analyzers/MirageAnalyzer.Serialization.cs b/CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.Serialization.cs similarity index 100% rename from Mirage.Analyzers/MirageAnalyzer.Serialization.cs rename to CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.Serialization.cs diff --git a/Mirage.Analyzers/MirageAnalyzer.SyncVars.cs b/CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.SyncVars.cs similarity index 100% rename from Mirage.Analyzers/MirageAnalyzer.SyncVars.cs rename to CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.SyncVars.cs diff --git a/Mirage.Analyzers/MirageAnalyzer.cs b/CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.cs similarity index 100% rename from Mirage.Analyzers/MirageAnalyzer.cs rename to CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.cs diff --git a/Mirage.Analyzers/MirageRules.cs b/CSharpAnalyzers/Mirage.Analyzers/MirageRules.cs similarity index 100% rename from Mirage.Analyzers/MirageRules.cs rename to CSharpAnalyzers/Mirage.Analyzers/MirageRules.cs diff --git a/Mirage.Analyzers/MirageSymbols.cs b/CSharpAnalyzers/Mirage.Analyzers/MirageSymbols.cs similarity index 100% rename from Mirage.Analyzers/MirageSymbols.cs rename to CSharpAnalyzers/Mirage.Analyzers/MirageSymbols.cs From ed4b817892938ceee4e19592793f677a14aa46ab Mon Sep 17 00:00:00 2001 From: James Frowen Date: Sun, 31 May 2026 20:25:37 +0100 Subject: [PATCH 21/41] test: Analyzer tests --- .../AwakeStartNetworkStateTests.cs | 134 +--------- .../DirectMutationTests.cs | 248 +----------------- .../FieldSerializationTests.cs | 195 +------------- .../Mirage.Analyzers.Tests.csproj | 7 + .../MismatchedSerializerTests.cs | 98 +------ .../MtuSizeEstimationTests.cs | 102 +------ .../OnSerializeBaseCallTests.cs | 153 +---------- .../RateLimitSettingsTests.cs | 136 +--------- .../Edge_NonSyncVarAccessInAwakeStart.cs | 13 + .../Negative_AccessIsServerInAwake.cs | 11 + .../Negative_AccessSyncVarFieldInAwake.cs | 12 + .../Negative_AccessSyncVarPropertyInStart.cs | 12 + .../Positive_AccessInAllowedMethods.cs | 27 ++ .../Positive_NonNetworkBehaviourClass.cs | 13 + ...igningEntireElementDoesNotReportWarning.cs | 18 ++ ...onOfSyncDictionaryElementReportsWarning.cs | 17 ++ ...MutationOfSyncListElementReportsWarning.cs | 17 ++ ...ionWithCompoundAssignmentReportsWarning.cs | 17 ++ ...tationWithUnaryExpressionReportsWarning.cs | 17 ++ ...ModifyingLocalArrayDoesNotReportWarning.cs | 15 ++ ...difyingStandardListDoesNotReportWarning.cs | 16 ++ ...estedMemberAccessMutationReportsWarning.cs | 22 ++ ...ngElementMemberAsRefParamReportsWarning.cs | 22 ++ ...eadingElementMemberDoesNotReportWarning.cs | 17 ++ ...tomTypeWithSerializerDoesNotReportError.cs | 19 ++ .../MultiDimensionalArrayReportsError.cs | 7 + ...mitiveAndSupportedTypesDoNotReportError.cs | 14 + .../PrivateFieldsAreIgnored.cs | 8 + .../RecursiveTypeReportsError.cs | 13 + ...ructWithUnserializableFieldReportsError.cs | 13 + .../UnserializableFieldReportsError.cs | 8 + .../UnserializablePropertyReportsError.cs | 8 + .../UnserializableRpcParameterReportsError.cs | 8 + ...UnserializableRpcReturnTypeReportsError.cs | 15 ++ ...lassWithUnserializableFieldReportsError.cs | 14 + ...tchingReaderAndWriterDoesNotReportError.cs | 12 + .../MismatchedArraySerializerReportsError.cs | 8 + ...assWithMismatchedSerializerReportsError.cs | 11 + .../NonExtensionMethodsAreIgnored.cs | 10 + .../ReaderOnlyReportsError.cs | 11 + .../WriterOnlyReportsError.cs | 11 + .../Edge_RecursiveStructOrClassRef.cs | 8 + .../Negative_LargeMessageExceedsMtu.cs | 16 ++ .../Positive_LargeMessageReducedByPacking.cs | 9 + ...itive_SmallMessageDoesNotTriggerWarning.cs | 10 + .../Edge_BaseSyncStateFromISyncObject.cs | 16 ++ .../Negative_MissingBaseOnDeserialize.cs | 18 ++ .../Negative_MissingBaseOnSerialize.cs | 20 ++ .../Positive_BaseCallIncluded.cs | 26 ++ .../Positive_NoBaseSyncState.cs | 23 ++ .../CustomRateLimitAttributeIgnored.cs | 20 ++ .../InvalidRateLimitInterval.cs | 10 + ...InvalidRateLimitMaxTokensLessThanRefill.cs | 10 + .../InvalidRateLimitRefillAndMaxTokens.cs | 10 + .../RateLimitOnNonRpcMethodIgnored.cs | 9 + .../ValidRateLimitDefaultSettings.cs | 10 + .../ValidRateLimitSettings.cs | 10 + .../InvalidClientRpcObserversWithUniTask.cs | 17 ++ ...ientRpcPlayerWithoutConnectionParameter.cs | 9 + .../ValidClientRpcObserversReturnsVoid.cs | 9 + .../ValidClientRpcOwnerWithUniTask.cs | 17 ++ ...lidClientRpcPlayerWithNetworkConnection.cs | 14 + .../ValidClientRpcPlayerWithNetworkPlayer.cs | 9 + .../Mirage.Analyzers.Tests/VerifyCS.cs | 9 + CSharpAnalyzers/TestCodeImprovement.md | 47 ++++ 65 files changed, 868 insertions(+), 1017 deletions(-) create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Edge_NonSyncVarAccessInAwakeStart.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Negative_AccessIsServerInAwake.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Negative_AccessSyncVarFieldInAwake.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Negative_AccessSyncVarPropertyInStart.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Positive_AccessInAllowedMethods.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Positive_NonNetworkBehaviourClass.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/AssigningEntireElementDoesNotReportWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/DirectMutationOfSyncDictionaryElementReportsWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/DirectMutationOfSyncListElementReportsWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/DirectMutationWithCompoundAssignmentReportsWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/DirectMutationWithUnaryExpressionReportsWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/ModifyingLocalArrayDoesNotReportWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/ModifyingStandardListDoesNotReportWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/NestedMemberAccessMutationReportsWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/PassingElementMemberAsRefParamReportsWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/ReadingElementMemberDoesNotReportWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/CustomTypeWithSerializerDoesNotReportError.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/MultiDimensionalArrayReportsError.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/PrimitiveAndSupportedTypesDoNotReportError.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/PrivateFieldsAreIgnored.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/RecursiveTypeReportsError.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/StructWithUnserializableFieldReportsError.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/UnserializableFieldReportsError.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/UnserializablePropertyReportsError.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/UnserializableRpcParameterReportsError.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/UnserializableRpcReturnTypeReportsError.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/WeaverSafeClassWithUnserializableFieldReportsError.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/MatchingReaderAndWriterDoesNotReportError.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/MismatchedArraySerializerReportsError.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/NestedStaticClassWithMismatchedSerializerReportsError.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/NonExtensionMethodsAreIgnored.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/ReaderOnlyReportsError.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/WriterOnlyReportsError.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Edge_RecursiveStructOrClassRef.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Negative_LargeMessageExceedsMtu.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Positive_LargeMessageReducedByPacking.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Positive_SmallMessageDoesNotTriggerWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/OnSerializeBaseCall/Edge_BaseSyncStateFromISyncObject.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/OnSerializeBaseCall/Negative_MissingBaseOnDeserialize.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/OnSerializeBaseCall/Negative_MissingBaseOnSerialize.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/OnSerializeBaseCall/Positive_BaseCallIncluded.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/OnSerializeBaseCall/Positive_NoBaseSyncState.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/CustomRateLimitAttributeIgnored.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/InvalidRateLimitInterval.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/InvalidRateLimitMaxTokensLessThanRefill.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/InvalidRateLimitRefillAndMaxTokens.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/RateLimitOnNonRpcMethodIgnored.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/ValidRateLimitDefaultSettings.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/ValidRateLimitSettings.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/InvalidClientRpcObserversWithUniTask.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/InvalidClientRpcPlayerWithoutConnectionParameter.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcObserversReturnsVoid.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcOwnerWithUniTask.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcPlayerWithNetworkConnection.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcPlayerWithNetworkPlayer.cs create mode 100644 CSharpAnalyzers/TestCodeImprovement.md diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/AwakeStartNetworkStateTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/AwakeStartNetworkStateTests.cs index 6991fd7e4be..cd44861d5e1 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/AwakeStartNetworkStateTests.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/AwakeStartNetworkStateTests.cs @@ -6,100 +6,24 @@ namespace Mirage.Analyzers.Tests [TestFixture] public class AwakeStartNetworkStateTests { - private const string MockDefinitions = @" -namespace Mirage -{ - public class NetworkBehaviour - { - public bool IsServer { get; } - public bool IsClient { get; } - public bool HasAuthority { get; } - public bool IsLocalPlayer { get; } - public bool IsOwner { get; } - public bool IsHost { get; } - } - public class SyncVarAttribute : System.Attribute {} -} -"; - [Test] public async Task Positive_AccessInAllowedMethods() { - // Verify that accessing network state in methods other than Awake/Start does not trigger warning - var code = @" -using Mirage; - -public class ValidBehaviour : NetworkBehaviour -{ - [SyncVar] - public int Health { get; set; } - - [SyncVar] - public int Points; - - public void Update() - { - if (IsServer) - { - Health = 100; - Points = 10; - } - } - - public override void OnStartServer() - { - if (IsClient) - { - var h = Health; - } - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("AwakeStart/Positive_AccessInAllowedMethods.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task Positive_NonNetworkBehaviourClass() { - // Verify that a class not inheriting from NetworkBehaviour can use Awake/Start with fields/properties of similar names - var code = @" -public class NonNetworkClass -{ - public bool IsServer { get; set; } - public int Health { get; set; } - - private void Awake() - { - if (IsServer) - { - Health = 100; - } - } -} -"; - + var code = VerifyCS.LoadTestData("AwakeStart/Positive_NonNetworkBehaviourClass.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task Negative_AccessIsServerInAwake() { - // Verify accessing IsServer in Awake triggers MIRAGE1401 warning - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - private void Awake() - { - if ({|#0:IsServer|}) - { - } - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("AwakeStart/Negative_AccessIsServerInAwake.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1401") .WithLocation(0) .WithArguments("IsServer", "Awake"); @@ -110,22 +34,7 @@ private void Awake() [Test] public async Task Negative_AccessSyncVarPropertyInStart() { - // Verify accessing SyncVar property in Start triggers MIRAGE1401 warning - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - [SyncVar] - public int Health { get; set; } - - private void Start() - { - {|#0:Health|} = 100; - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("AwakeStart/Negative_AccessSyncVarPropertyInStart.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1401") .WithLocation(0) .WithArguments("Health", "Start"); @@ -136,22 +45,7 @@ private void Start() [Test] public async Task Negative_AccessSyncVarFieldInAwake() { - // Verify accessing SyncVar field in Awake triggers MIRAGE1401 warning - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - [SyncVar] - public int points; - - private void Awake() - { - var p = {|#0:points|}; - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("AwakeStart/Negative_AccessSyncVarFieldInAwake.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1401") .WithLocation(0) .WithArguments("points", "Awake"); @@ -162,23 +56,7 @@ private void Awake() [Test] public async Task Edge_NonSyncVarAccessInAwakeStart() { - // Verify that non-SyncVar fields and properties can be accessed in Awake/Start - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - public int NonSyncField; - public int NonSyncProp { get; set; } - - private void Awake() - { - NonSyncField = 5; - NonSyncProp = 10; - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("AwakeStart/Edge_NonSyncVarAccessInAwakeStart.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } } diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/DirectMutationTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/DirectMutationTests.cs index 5dae802f171..2ed0c89ccbf 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/DirectMutationTests.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/DirectMutationTests.cs @@ -6,156 +6,38 @@ namespace Mirage.Analyzers.Tests [TestFixture] public class DirectMutationTests { - private const string MockDefinitions = @" -namespace Mirage -{ - public class NetworkBehaviour {} - public class WeaverSafeClassAttribute : System.Attribute {} -} -namespace Mirage.Collections -{ - public interface ISyncObject {} - public class SyncList : ISyncObject - { - public T this[int index] { get => default; set {} } - } - public class SyncDictionary : ISyncObject - { - public TValue this[TKey key] { get => default; set {} } - } -} -"; - [Test] public async Task ModifyingLocalArrayDoesNotReportWarning() { - // Verify that modifying standard local array elements does not trigger MIRAGE1003 - var code = @" -using Mirage; - -public class MyClass -{ - public int Value; -} - -public class MyBehaviour : NetworkBehaviour -{ - public void Modify() - { - var array = new MyClass[5]; - array[0].Value = 10; - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("DirectMutation/ModifyingLocalArrayDoesNotReportWarning.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task ModifyingStandardListDoesNotReportWarning() { - // Verify that modifying standard System.Collections.Generic.List elements does not trigger MIRAGE1003 - var code = @" -using System.Collections.Generic; -using Mirage; - -public class MyClass -{ - public int Value; -} - -public class MyBehaviour : NetworkBehaviour -{ - public void Modify() - { - var list = new List(); - list[0].Value = 10; - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("DirectMutation/ModifyingStandardListDoesNotReportWarning.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task AssigningEntireElementDoesNotReportWarning() { - // Verify that replacing the entire element in SyncList is allowed and does not trigger MIRAGE1003 - var code = @" -using Mirage; -using Mirage.Collections; - -[WeaverSafeClass] -public struct MyStruct -{ - public int Value; -} - -public class MyBehaviour : NetworkBehaviour -{ - public readonly SyncList mySyncList = new SyncList(); - - public void Modify() - { - mySyncList[0] = new MyStruct { Value = 10 }; - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("DirectMutation/AssigningEntireElementDoesNotReportWarning.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task ReadingElementMemberDoesNotReportWarning() { - // Verify that reading a member of an element does not trigger MIRAGE1003 - var code = @" -using Mirage; -using Mirage.Collections; - -public class MyClass -{ - public int Value; -} - -public class MyBehaviour : NetworkBehaviour -{ - public readonly SyncList mySyncList = new SyncList(); - - public void Modify() - { - int val = mySyncList[0].Value; - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("DirectMutation/ReadingElementMemberDoesNotReportWarning.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task DirectMutationOfSyncListElementReportsWarning() { - // Verify that directly mutating a field of a SyncList element triggers MIRAGE1003 - var code = @" -using Mirage; -using Mirage.Collections; - -public class MyClass -{ - public int Value; -} - -public class MyBehaviour : NetworkBehaviour -{ - public readonly SyncList mySyncList = new SyncList(); - - public void Modify() - { - {|#0:mySyncList[0]|}.Value = 10; - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("DirectMutation/DirectMutationOfSyncListElementReportsWarning.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1003").WithLocation(0).WithArguments("mySyncList"); await VerifyCS.VerifyAnalyzerAsync(code, expected); } @@ -163,27 +45,7 @@ public void Modify() [Test] public async Task DirectMutationOfSyncDictionaryElementReportsWarning() { - // Verify that directly mutating a field of a SyncDictionary element triggers MIRAGE1003 - var code = @" -using Mirage; -using Mirage.Collections; - -public class MyClass -{ - public int Value; -} - -public class MyBehaviour : NetworkBehaviour -{ - public readonly SyncDictionary mySyncDict = new SyncDictionary(); - - public void Modify() - { - {|#0:mySyncDict[1]|}.Value = 10; - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("DirectMutation/DirectMutationOfSyncDictionaryElementReportsWarning.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1003").WithLocation(0).WithArguments("mySyncDict"); await VerifyCS.VerifyAnalyzerAsync(code, expected); } @@ -191,27 +53,7 @@ public void Modify() [Test] public async Task DirectMutationWithCompoundAssignmentReportsWarning() { - // Verify that compound assignments like += on elements trigger MIRAGE1003 - var code = @" -using Mirage; -using Mirage.Collections; - -public class MyClass -{ - public int Value; -} - -public class MyBehaviour : NetworkBehaviour -{ - public readonly SyncList mySyncList = new SyncList(); - - public void Modify() - { - {|#0:mySyncList[0]|}.Value += 5; - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("DirectMutation/DirectMutationWithCompoundAssignmentReportsWarning.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1003").WithLocation(0).WithArguments("mySyncList"); await VerifyCS.VerifyAnalyzerAsync(code, expected); } @@ -219,27 +61,7 @@ public void Modify() [Test] public async Task DirectMutationWithUnaryExpressionReportsWarning() { - // Verify that unary operators like ++ trigger MIRAGE1003 - var code = @" -using Mirage; -using Mirage.Collections; - -public class MyClass -{ - public int Value; -} - -public class MyBehaviour : NetworkBehaviour -{ - public readonly SyncList mySyncList = new SyncList(); - - public void Modify() - { - {|#0:mySyncList[0]|}.Value++; - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("DirectMutation/DirectMutationWithUnaryExpressionReportsWarning.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1003").WithLocation(0).WithArguments("mySyncList"); await VerifyCS.VerifyAnalyzerAsync(code, expected); } @@ -247,32 +69,7 @@ public void Modify() [Test] public async Task PassingElementMemberAsRefParamReportsWarning() { - // Verify that passing an element member by reference triggers MIRAGE1003 - var code = @" -using Mirage; -using Mirage.Collections; - -public class MyClass -{ - public int Value; -} - -public class MyBehaviour : NetworkBehaviour -{ - public readonly SyncList mySyncList = new SyncList(); - - public void Modify() - { - Helper(ref {|#0:mySyncList[0]|}.Value); - } - - private void Helper(ref int val) - { - val = 5; - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("DirectMutation/PassingElementMemberAsRefParamReportsWarning.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1003").WithLocation(0).WithArguments("mySyncList"); await VerifyCS.VerifyAnalyzerAsync(code, expected); } @@ -280,32 +77,7 @@ private void Helper(ref int val) [Test] public async Task NestedMemberAccessMutationReportsWarning() { - // Verify that deeply nested member access mutation also triggers MIRAGE1003 - var code = @" -using Mirage; -using Mirage.Collections; - -public class NestedClass -{ - public int Value; -} - -public class MyClass -{ - public NestedClass Nested = new NestedClass(); -} - -public class MyBehaviour : NetworkBehaviour -{ - public readonly SyncList mySyncList = new SyncList(); - - public void Modify() - { - {|#0:mySyncList[0]|}.Nested.Value = 10; - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("DirectMutation/NestedMemberAccessMutationReportsWarning.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1003").WithLocation(0).WithArguments("mySyncList"); await VerifyCS.VerifyAnalyzerAsync(code, expected); } diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/FieldSerializationTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/FieldSerializationTests.cs index 3cc737566de..ce1bcc1f756 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/FieldSerializationTests.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/FieldSerializationTests.cs @@ -6,122 +6,35 @@ namespace Mirage.Analyzers.Tests [TestFixture] public class FieldSerializationTests { - private const string MockDefinitions = @" -namespace Mirage -{ - public class NetworkMessageAttribute : System.Attribute {} - public class WeaverSafeClassAttribute : System.Attribute {} - public class ServerRpcAttribute : System.Attribute {} - public class ClientRpcAttribute : System.Attribute {} - public class NetworkBehaviour {} -} - -namespace Mirage.Serialization -{ - public class NetworkWriter {} - public class NetworkReader {} -} - -namespace Cysharp.Threading.Tasks -{ - public struct UniTask {} - public struct UniTask {} -} - -namespace UnityEngine -{ - public class GameObject {} - public struct Vector3 {} -} -"; - [Test] public async Task PrimitiveAndSupportedTypesDoNotReportError() { - var code = @" -using Mirage; -using UnityEngine; -using System; - -[NetworkMessage] -public struct ValidMessage -{ - public int myInt; - public string myString; - public Vector3 myVector; - public Guid myGuid; - public DateTime myDateTime; - public byte[] myByteArray; -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("FieldSerialization/PrimitiveAndSupportedTypesDoNotReportError.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task CustomTypeWithSerializerDoesNotReportError() { - var code = @" -using Mirage; -using Mirage.Serialization; - -public struct CustomType -{ - public int value; -} - -public static class CustomSerialization -{ - public static void WriteCustomType(this NetworkWriter writer, CustomType value) {} - public static CustomType ReadCustomType(this NetworkReader reader) => default; -} - -[NetworkMessage] -public struct ValidMessage -{ - public CustomType customValue; -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("FieldSerialization/CustomTypeWithSerializerDoesNotReportError.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task PrivateFieldsAreIgnored() { - var code = @" -using Mirage; -using System.Threading; - -[NetworkMessage] -public struct MessageWithPrivateField -{ - private Thread executionThread; -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("FieldSerialization/PrivateFieldsAreIgnored.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task UnserializableFieldReportsError() { - var code = @" -using Mirage; -using System.Threading; - -[NetworkMessage] -public struct StartSessionMessage -{ - public Thread {|#0:executionThread|}; -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("FieldSerialization/UnserializableFieldReportsError.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1302") .WithLocation(0) .WithArguments("Thread", "NetworkMessage field"); - // Note: Since Thread is a class type without WeaverSafeClass attribute, it also triggers MIRAGE1301. var expectedClassWarning = VerifyCS.Diagnostic("MIRAGE1301") .WithLocation(0) .WithArguments("NetworkMessage field", "executionThread", "Thread"); @@ -132,17 +45,7 @@ public struct StartSessionMessage [Test] public async Task UnserializablePropertyReportsError() { - var code = @" -using Mirage; -using System.Threading; - -[NetworkMessage] -public struct StartSessionMessage -{ - public Thread {|#0:ExecutionThread|} { get; set; } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("FieldSerialization/UnserializablePropertyReportsError.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1302") .WithLocation(0) .WithArguments("Thread", "NetworkMessage property"); @@ -157,17 +60,7 @@ public struct StartSessionMessage [Test] public async Task UnserializableRpcParameterReportsError() { - var code = @" -using Mirage; -using System.Threading; - -public class MyBehaviour : NetworkBehaviour -{ - [ServerRpc] - public void CmdStartSession(Thread {|#0:executionThread|}) {} -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("FieldSerialization/UnserializableRpcParameterReportsError.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1302") .WithLocation(0) .WithArguments("Thread", "RPC parameter"); @@ -182,18 +75,7 @@ public void CmdStartSession(Thread {|#0:executionThread|}) {} [Test] public async Task UnserializableRpcReturnTypeReportsError() { - var code = @" -using Mirage; -using System.Threading; -using Cysharp.Threading.Tasks; - -public class MyBehaviour : NetworkBehaviour -{ - [ServerRpc] - public UniTask {|#0:CmdGetSession|}() => default; -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("FieldSerialization/UnserializableRpcReturnTypeReportsError.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1302") .WithLocation(0) .WithArguments("Thread", "RPC return type"); @@ -208,22 +90,7 @@ public class MyBehaviour : NetworkBehaviour [Test] public async Task StructWithUnserializableFieldReportsError() { - var code = @" -using Mirage; -using System.Threading; - -public struct NestedUnserializable -{ - public Thread threadField; -} - -[NetworkMessage] -public struct MainMessage -{ - public NestedUnserializable {|#0:nestedField|}; -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("FieldSerialization/StructWithUnserializableFieldReportsError.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1302") .WithLocation(0) .WithArguments("NestedUnserializable", "NetworkMessage field"); @@ -234,22 +101,7 @@ public struct MainMessage [Test] public async Task RecursiveTypeReportsError() { - var code = @" -using Mirage; - -[WeaverSafeClass] -public class RecursiveClass -{ - public RecursiveClass {|#0:self|}; -} - -[NetworkMessage] -public struct Message -{ - public RecursiveClass {|#1:recursiveField|}; -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("FieldSerialization/RecursiveTypeReportsError.cs"); var expected1 = VerifyCS.Diagnostic("MIRAGE1302") .WithLocation(0) .WithArguments("RecursiveClass", "NetworkMessage field"); @@ -264,16 +116,7 @@ public struct Message [Test] public async Task MultiDimensionalArrayReportsError() { - var code = @" -using Mirage; - -[NetworkMessage] -public struct MessageWithMultiArray -{ - public int[,] {|#0:multiArray|}; -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("FieldSerialization/MultiDimensionalArrayReportsError.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1302") .WithLocation(0) .WithArguments("Int32", "NetworkMessage field"); @@ -284,23 +127,7 @@ public struct MessageWithMultiArray [Test] public async Task WeaverSafeClassWithUnserializableFieldReportsError() { - var code = @" -using Mirage; -using System.Threading; - -[WeaverSafeClass] -public class SafeClassWithThread -{ - public Thread threadField; -} - -[NetworkMessage] -public struct Message -{ - public SafeClassWithThread {|#0:safeClassField|}; -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("FieldSerialization/WeaverSafeClassWithUnserializableFieldReportsError.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1302") .WithLocation(0) .WithArguments("SafeClassWithThread", "NetworkMessage field"); diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/Mirage.Analyzers.Tests.csproj b/CSharpAnalyzers/Mirage.Analyzers.Tests/Mirage.Analyzers.Tests.csproj index 682efa3cfab..485d97d967b 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/Mirage.Analyzers.Tests.csproj +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/Mirage.Analyzers.Tests.csproj @@ -17,4 +17,11 @@ + + + + + PreserveNewest + + diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/MismatchedSerializerTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/MismatchedSerializerTests.cs index 0e253bfff84..ceb3a3a478e 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/MismatchedSerializerTests.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/MismatchedSerializerTests.cs @@ -6,73 +6,24 @@ namespace Mirage.Analyzers.Tests [TestFixture] public class MismatchedSerializerTests { - private const string MockDefinitions = @" -namespace Mirage.Serialization -{ - public class NetworkWriter {} - public class NetworkReader {} -} -"; - [Test] public async Task MatchingReaderAndWriterDoesNotReportError() { - var code = @" -using Mirage.Serialization; - -public struct CustomType -{ - public int value; -} - -public static class CustomSerialization -{ - public static void WriteCustomType(this NetworkWriter writer, CustomType value) {} - public static CustomType ReadCustomType(this NetworkReader reader) => default; -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("MismatchedSerializer/MatchingReaderAndWriterDoesNotReportError.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task NonExtensionMethodsAreIgnored() { - // Non-extension methods should not be treated as custom serializers, - // so they should not trigger any mismatched serialization warnings. - var code = @" -using Mirage.Serialization; - -public struct CustomType {} - -public static class CustomSerialization -{ - // Not extension methods (missing 'this') - public static void WriteCustomType(NetworkWriter writer, CustomType value) {} - public static CustomType ReadCustomType(NetworkReader reader) => default; -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("MismatchedSerializer/NonExtensionMethodsAreIgnored.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task WriterOnlyReportsError() { - var code = @" -using Mirage.Serialization; - -public struct CustomType -{ - public int value; -} - -public static class CustomSerialization -{ - public static void {|#0:WriteCustomType|}(this NetworkWriter writer, CustomType value) {} -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("MismatchedSerializer/WriterOnlyReportsError.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1303") .WithLocation(0) .WithArguments("CustomType", "Custom writer defined for 'CustomType' but matching custom reader is missing."); @@ -83,20 +34,7 @@ public static class CustomSerialization [Test] public async Task ReaderOnlyReportsError() { - var code = @" -using Mirage.Serialization; - -public struct CustomType -{ - public int value; -} - -public static class CustomSerialization -{ - public static CustomType {|#0:ReadCustomType|}(this NetworkReader reader) => default; -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("MismatchedSerializer/ReaderOnlyReportsError.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1303") .WithLocation(0) .WithArguments("CustomType", "Custom reader defined for 'CustomType' but matching custom writer is missing."); @@ -107,20 +45,7 @@ public static class CustomSerialization [Test] public async Task NestedStaticClassWithMismatchedSerializerReportsError() { - var code = @" -using Mirage.Serialization; - -public struct CustomType {} - -public static class OuterClass -{ - public static class InnerClass - { - public static void {|#0:WriteCustomType|}(this NetworkWriter writer, CustomType value) {} - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("MismatchedSerializer/NestedStaticClassWithMismatchedSerializerReportsError.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1303") .WithLocation(0) .WithArguments("CustomType", "Custom writer defined for 'CustomType' but matching custom reader is missing."); @@ -131,18 +56,7 @@ public static class InnerClass [Test] public async Task MismatchedArraySerializerReportsError() { - // Custom serialization for arrays (e.g. CustomType[]) must also match - var code = @" -using Mirage.Serialization; - -public struct CustomType {} - -public static class CustomSerialization -{ - public static void {|#0:WriteCustomArray|}(this NetworkWriter writer, CustomType[] value) {} -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("MismatchedSerializer/MismatchedArraySerializerReportsError.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1303") .WithLocation(0) .WithArguments("CustomType[]", "Custom writer defined for 'CustomType[]' but matching custom reader is missing."); diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/MtuSizeEstimationTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/MtuSizeEstimationTests.cs index 93c0789cadd..076305283f1 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/MtuSizeEstimationTests.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/MtuSizeEstimationTests.cs @@ -6,107 +6,24 @@ namespace Mirage.Analyzers.Tests [TestFixture] public class MtuSizeEstimationTests { - private const string MockDefinitions = @" -namespace Mirage -{ - public class NetworkMessageAttribute : System.Attribute {} - public class NetworkBehaviour {} - public class NetworkIdentity {} -} -namespace Mirage.Serialization -{ - public class BitCountAttribute : System.Attribute - { - public BitCountAttribute(int bits) {} - } - public class FloatPackAttribute : System.Attribute - { - public FloatPackAttribute(float min, float max, float precision) {} - public FloatPackAttribute(int bits) {} // overload for testing - public FloatPackAttribute(float max, int bits) {} // overload matching signature in analyzer - } -} -namespace UnityEngine -{ - public struct Vector3 - { - public float x; - public float y; - public float z; - } - public struct Quaternion - { - public float x; - public float y; - public float z; - public float w; - } -} -"; - [Test] public async Task Positive_SmallMessageDoesNotTriggerWarning() { - // Verify that a small network message does not trigger MTU warning (estimated size <= 1200) - var code = @" -using Mirage; -using UnityEngine; - -[NetworkMessage] -public struct SmallMessage -{ - public int id; - public Vector3 position; - public string name; -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("MtuSizeEstimation/Positive_SmallMessageDoesNotTriggerWarning.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task Positive_LargeMessageReducedByPacking() { - // Verify that bitcount / floatpack attribute overrides size estimation, keeping it within safe MTU - var code = @" -using Mirage; -using Mirage.Serialization; - -[NetworkMessage] -public struct PackedTestMessage -{ - [FloatPack(0.0f, 1)] // Should estimate size based on 1 bit instead of 4/8 bytes - public float CompressedVal; -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("MtuSizeEstimation/Positive_LargeMessageReducedByPacking.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task Negative_LargeMessageExceedsMtu() { - // Verify that a network message exceeding 1200 bytes triggers MIRAGE1501 warning. - // 10 arrays of double: 10 * 128 * 8 = 10240 bytes. - var code = @" -using Mirage; - -[NetworkMessage] -public struct {|#0:HugeMessage|} -{ - public double[] arr1; - public double[] arr2; - public double[] arr3; - public double[] arr4; - public double[] arr5; - public double[] arr6; - public double[] arr7; - public double[] arr8; - public double[] arr9; - public double[] arr10; -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("MtuSizeEstimation/Negative_LargeMessageExceedsMtu.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1501") .WithLocation(0) .WithArguments("HugeMessage", "10240", "1200"); @@ -117,18 +34,7 @@ public struct {|#0:HugeMessage|} [Test] public async Task Edge_RecursiveStructOrClassRef() { - // Verify that recursive types do not cause infinite recursion and resolve to 0 or valid size - var code = @" -using Mirage; - -[NetworkMessage] -public class RecursiveMessage -{ - public RecursiveMessage Parent; - public int Value; -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("MtuSizeEstimation/Edge_RecursiveStructOrClassRef.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } } diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/OnSerializeBaseCallTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/OnSerializeBaseCallTests.cs index 53145f4deef..4d8bb2c616c 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/OnSerializeBaseCallTests.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/OnSerializeBaseCallTests.cs @@ -6,127 +6,24 @@ namespace Mirage.Analyzers.Tests [TestFixture] public class OnSerializeBaseCallTests { - private const string MockDefinitions = @" -namespace Mirage -{ - public class NetworkBehaviour - { - public virtual bool OnSerialize(Mirage.Serialization.NetworkWriter writer, bool initialState) => true; - public virtual void OnDeserialize(Mirage.Serialization.NetworkReader reader, bool initialState) {} - } - public class SyncVarAttribute : System.Attribute {} -} -namespace Mirage.Serialization -{ - public class NetworkWriter - { - public void WritePackedInt32(int value) {} - } - public class NetworkReader {} -} -namespace Mirage.Collections -{ - public interface ISyncObject {} - public class SyncList : ISyncObject {} -} -"; - [Test] public async Task Positive_BaseCallIncluded() { - // Verify that calling base.OnSerialize and base.OnDeserialize when base has sync state compiles and triggers no warning - var code = @" -using Mirage; -using Mirage.Serialization; - -public class BasePlayer : NetworkBehaviour -{ - [SyncVar] - public string PlayerName { get; set; } -} - -public class HeroPlayer : BasePlayer -{ - [SyncVar] - public int HeroId { get; set; } - - public override bool OnSerialize(NetworkWriter writer, bool initialState) - { - bool baseDirty = base.OnSerialize(writer, initialState); - writer.WritePackedInt32(HeroId); - return baseDirty || true; - } - - public override void OnDeserialize(NetworkReader reader, bool initialState) - { - base.OnDeserialize(reader, initialState); - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("OnSerializeBaseCall/Positive_BaseCallIncluded.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task Positive_NoBaseSyncState() { - // Verify that overriding OnSerialize/OnDeserialize without calling base does not trigger warning if base has no sync state - var code = @" -using Mirage; -using Mirage.Serialization; - -public class EmptyBase : NetworkBehaviour -{ - // No SyncVars or ISyncObjects here -} - -public class DerivedPlayer : EmptyBase -{ - [SyncVar] - public int HeroId { get; set; } - - public override bool OnSerialize(NetworkWriter writer, bool initialState) - { - writer.WritePackedInt32(HeroId); - return true; - } - - public override void OnDeserialize(NetworkReader reader, bool initialState) - { - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("OnSerializeBaseCall/Positive_NoBaseSyncState.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task Negative_MissingBaseOnSerialize() { - // Verify that missing base.OnSerialize when base has sync state triggers MIRAGE1402 warning - var code = @" -using Mirage; -using Mirage.Serialization; - -public class BasePlayer : NetworkBehaviour -{ - [SyncVar] - public string PlayerName { get; set; } -} - -public class HeroPlayer : BasePlayer -{ - [SyncVar] - public int HeroId { get; set; } - - public override bool {|#0:OnSerialize|}(NetworkWriter writer, bool initialState) - { - writer.WritePackedInt32(HeroId); - return true; - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("OnSerializeBaseCall/Negative_MissingBaseOnSerialize.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1402") .WithLocation(0) .WithArguments("OnSerialize"); @@ -137,28 +34,7 @@ public class HeroPlayer : BasePlayer [Test] public async Task Negative_MissingBaseOnDeserialize() { - // Verify that missing base.OnDeserialize when base has sync state triggers MIRAGE1402 warning - var code = @" -using Mirage; -using Mirage.Serialization; - -public class BasePlayer : NetworkBehaviour -{ - [SyncVar] - public string PlayerName { get; set; } -} - -public class HeroPlayer : BasePlayer -{ - [SyncVar] - public int HeroId { get; set; } - - public override void {|#0:OnDeserialize|}(NetworkReader reader, bool initialState) - { - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("OnSerializeBaseCall/Negative_MissingBaseOnDeserialize.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1402") .WithLocation(0) .WithArguments("OnDeserialize"); @@ -169,26 +45,7 @@ public class HeroPlayer : BasePlayer [Test] public async Task Edge_BaseSyncStateFromISyncObject() { - // Verify warning is triggered if base class only has ISyncObject (like SyncList) and base call is missing - var code = @" -using Mirage; -using Mirage.Serialization; -using Mirage.Collections; - -public class BasePlayer : NetworkBehaviour -{ - public SyncList Scores = new SyncList(); -} - -public class HeroPlayer : BasePlayer -{ - public override bool {|#0:OnSerialize|}(NetworkWriter writer, bool initialState) - { - return true; - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("OnSerializeBaseCall/Edge_BaseSyncStateFromISyncObject.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1402") .WithLocation(0) .WithArguments("OnSerialize"); diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/RateLimitSettingsTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/RateLimitSettingsTests.cs index 938d6bb6faa..6455cb6cc42 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/RateLimitSettingsTests.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/RateLimitSettingsTests.cs @@ -6,84 +6,24 @@ namespace Mirage.Analyzers.Tests [TestFixture] public class RateLimitSettingsTests { - private const string MockDefinitions = @" -namespace Mirage -{ - public class NetworkBehaviour {} - - [System.AttributeUsage(System.AttributeTargets.Method)] - public class ServerRpcAttribute : System.Attribute {} - - [System.AttributeUsage(System.AttributeTargets.Method)] - public class ClientRpcAttribute : System.Attribute {} - - [System.AttributeUsage(System.AttributeTargets.Method)] - public class RateLimitAttribute : System.Attribute - { - public float Interval { get; set; } - public int Refill { get; set; } - public int MaxTokens { get; set; } - } -} -"; - [Test] public async Task ValidRateLimitSettings() { - // Verify rate limit settings with positive values and MaxTokens >= Refill compile and analyze cleanly - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - [ServerRpc] - [RateLimit(Interval = 0.5f, Refill = 5, MaxTokens = 10)] - public void CmdFire() - { - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("RateLimitSettings/ValidRateLimitSettings.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task ValidRateLimitDefaultSettings() { - // Verify default rate limit settings are automatically treated as valid - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - [ServerRpc] - [RateLimit] - public void CmdFire() - { - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("RateLimitSettings/ValidRateLimitDefaultSettings.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task InvalidRateLimitInterval() { - // Verify an interval of zero or lower triggers the appropriate validation warning - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - [ServerRpc] - [RateLimit(Interval = 0f)] - public void {|#0:CmdFire|}() - { - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("RateLimitSettings/InvalidRateLimitInterval.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1205") .WithLocation(0) .WithArguments("CmdFire", "Interval must be greater than zero"); @@ -93,20 +33,7 @@ public class MyBehaviour : NetworkBehaviour [Test] public async Task InvalidRateLimitRefillAndMaxTokens() { - // Verify non-positive refills or tokens trigger warnings - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - [ServerRpc] - [RateLimit(Refill = -5, MaxTokens = 0)] - public void {|#0:CmdFire|}() - { - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("RateLimitSettings/InvalidRateLimitRefillAndMaxTokens.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1205") .WithLocation(0) .WithArguments("CmdFire", "Refill must be greater than zero, MaxTokens must be greater than zero"); @@ -116,20 +43,7 @@ public class MyBehaviour : NetworkBehaviour [Test] public async Task InvalidRateLimitMaxTokensLessThanRefill() { - // Verify that maximum allowed tokens cannot be configured below the refill amount - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - [ServerRpc] - [RateLimit(Refill = 10, MaxTokens = 5)] - public void {|#0:CmdFire|}() - { - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("RateLimitSettings/InvalidRateLimitMaxTokensLessThanRefill.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1205") .WithLocation(0) .WithArguments("CmdFire", "MaxTokens must be greater than or equal to Refill"); @@ -139,50 +53,14 @@ public class MyBehaviour : NetworkBehaviour [Test] public async Task RateLimitOnNonRpcMethodIgnored() { - // Verify that rate limit constraints are only enforced on network RPC methods - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - [RateLimit(Interval = 0f, Refill = 0, MaxTokens = 0)] - public void LocalFire() - { - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("RateLimitSettings/RateLimitOnNonRpcMethodIgnored.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task CustomRateLimitAttributeIgnored() { - // Verify external rate limiting attributes do not trigger Mirage-specific validations - var code = @" -using Mirage; - -namespace CustomNamespace -{ - public class RateLimitAttribute : System.Attribute - { - public float Interval { get; set; } - public int Refill { get; set; } - public int MaxTokens { get; set; } - } -} - -public class MyBehaviour : NetworkBehaviour -{ - [ServerRpc] - [CustomNamespace.RateLimit(Interval = -1f)] - public void CmdFire() - { - } -} -" + MockDefinitions; - - // Note: Since Mirage's [RateLimit] is missing on ServerRpc here, it will trigger MIRAGE1206 + var code = VerifyCS.LoadTestData("RateLimitSettings/CustomRateLimitAttributeIgnored.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1206") .WithLocation(0) .WithArguments("CmdFire"); diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Edge_NonSyncVarAccessInAwakeStart.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Edge_NonSyncVarAccessInAwakeStart.cs new file mode 100644 index 00000000000..8d3496f55b0 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Edge_NonSyncVarAccessInAwakeStart.cs @@ -0,0 +1,13 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + public int NonSyncField; + public int NonSyncProp { get; set; } + + private void Awake() + { + NonSyncField = 5; + NonSyncProp = 10; + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Negative_AccessIsServerInAwake.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Negative_AccessIsServerInAwake.cs new file mode 100644 index 00000000000..08c89919b45 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Negative_AccessIsServerInAwake.cs @@ -0,0 +1,11 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + private void Awake() + { + if ({|#0:IsServer|}) + { + } + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Negative_AccessSyncVarFieldInAwake.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Negative_AccessSyncVarFieldInAwake.cs new file mode 100644 index 00000000000..07a08c43cbb --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Negative_AccessSyncVarFieldInAwake.cs @@ -0,0 +1,12 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [SyncVar] + public int points { get; set; } + + private void Awake() + { + var p = {|#0:points|}; + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Negative_AccessSyncVarPropertyInStart.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Negative_AccessSyncVarPropertyInStart.cs new file mode 100644 index 00000000000..e856a6010ed --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Negative_AccessSyncVarPropertyInStart.cs @@ -0,0 +1,12 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [SyncVar] + public int Health { get; set; } + + private void Start() + { + {|#0:Health|} = 100; + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Positive_AccessInAllowedMethods.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Positive_AccessInAllowedMethods.cs new file mode 100644 index 00000000000..da5fe308ac0 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Positive_AccessInAllowedMethods.cs @@ -0,0 +1,27 @@ +using Mirage; + +public class ValidBehaviour : NetworkBehaviour +{ + [SyncVar] + public int Health { get; set; } + + [SyncVar] + public int Points; + + public void Update() + { + if (IsServer) + { + Health = 100; + Points = 10; + } + } + + public void OnStartServer() + { + if (IsClient) + { + var h = Health; + } + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Positive_NonNetworkBehaviourClass.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Positive_NonNetworkBehaviourClass.cs new file mode 100644 index 00000000000..7010178b6b0 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Positive_NonNetworkBehaviourClass.cs @@ -0,0 +1,13 @@ +public class NonNetworkClass +{ + public bool IsServer { get; set; } + public int Health { get; set; } + + private void Awake() + { + if (IsServer) + { + Health = 100; + } + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/AssigningEntireElementDoesNotReportWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/AssigningEntireElementDoesNotReportWarning.cs new file mode 100644 index 00000000000..7ae968f3102 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/AssigningEntireElementDoesNotReportWarning.cs @@ -0,0 +1,18 @@ +using Mirage; +using Mirage.Collections; + +[WeaverSafeClass] +public struct MyStruct +{ + public int Value; +} + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncList mySyncList = new SyncList(); + + public void Modify() + { + mySyncList[0] = new MyStruct { Value = 10 }; + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/DirectMutationOfSyncDictionaryElementReportsWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/DirectMutationOfSyncDictionaryElementReportsWarning.cs new file mode 100644 index 00000000000..a432fa4b8b3 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/DirectMutationOfSyncDictionaryElementReportsWarning.cs @@ -0,0 +1,17 @@ +using Mirage; +using Mirage.Collections; + +public class MyClass +{ + public int Value; +} + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncDictionary mySyncDict = new SyncDictionary(); + + public void Modify() + { + {|#0:mySyncDict[1]|}.Value = 10; + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/DirectMutationOfSyncListElementReportsWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/DirectMutationOfSyncListElementReportsWarning.cs new file mode 100644 index 00000000000..b95cff45bae --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/DirectMutationOfSyncListElementReportsWarning.cs @@ -0,0 +1,17 @@ +using Mirage; +using Mirage.Collections; + +public class MyClass +{ + public int Value; +} + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncList mySyncList = new SyncList(); + + public void Modify() + { + {|#0:mySyncList[0]|}.Value = 10; + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/DirectMutationWithCompoundAssignmentReportsWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/DirectMutationWithCompoundAssignmentReportsWarning.cs new file mode 100644 index 00000000000..a21a73d4eac --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/DirectMutationWithCompoundAssignmentReportsWarning.cs @@ -0,0 +1,17 @@ +using Mirage; +using Mirage.Collections; + +public class MyClass +{ + public int Value; +} + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncList mySyncList = new SyncList(); + + public void Modify() + { + {|#0:mySyncList[0]|}.Value += 5; + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/DirectMutationWithUnaryExpressionReportsWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/DirectMutationWithUnaryExpressionReportsWarning.cs new file mode 100644 index 00000000000..db043c9c228 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/DirectMutationWithUnaryExpressionReportsWarning.cs @@ -0,0 +1,17 @@ +using Mirage; +using Mirage.Collections; + +public class MyClass +{ + public int Value; +} + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncList mySyncList = new SyncList(); + + public void Modify() + { + {|#0:mySyncList[0]|}.Value++; + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/ModifyingLocalArrayDoesNotReportWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/ModifyingLocalArrayDoesNotReportWarning.cs new file mode 100644 index 00000000000..ca09721f684 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/ModifyingLocalArrayDoesNotReportWarning.cs @@ -0,0 +1,15 @@ +using Mirage; + +public class MyClass +{ + public int Value; +} + +public class MyBehaviour : NetworkBehaviour +{ + public void Modify() + { + var array = new MyClass[5]; + array[0].Value = 10; + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/ModifyingStandardListDoesNotReportWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/ModifyingStandardListDoesNotReportWarning.cs new file mode 100644 index 00000000000..a0d0c043519 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/ModifyingStandardListDoesNotReportWarning.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using Mirage; + +public class MyClass +{ + public int Value; +} + +public class MyBehaviour : NetworkBehaviour +{ + public void Modify() + { + var list = new List(); + list[0].Value = 10; + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/NestedMemberAccessMutationReportsWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/NestedMemberAccessMutationReportsWarning.cs new file mode 100644 index 00000000000..4cd35bb5ab6 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/NestedMemberAccessMutationReportsWarning.cs @@ -0,0 +1,22 @@ +using Mirage; +using Mirage.Collections; + +public class NestedClass +{ + public int Value; +} + +public class MyClass +{ + public NestedClass Nested = new NestedClass(); +} + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncList mySyncList = new SyncList(); + + public void Modify() + { + {|#0:mySyncList[0]|}.Nested.Value = 10; + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/PassingElementMemberAsRefParamReportsWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/PassingElementMemberAsRefParamReportsWarning.cs new file mode 100644 index 00000000000..814d90f9148 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/PassingElementMemberAsRefParamReportsWarning.cs @@ -0,0 +1,22 @@ +using Mirage; +using Mirage.Collections; + +public class MyClass +{ + public int Value; +} + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncList mySyncList = new SyncList(); + + public void Modify() + { + Helper(ref {|#0:mySyncList[0]|}.Value); + } + + private void Helper(ref int val) + { + val = 5; + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/ReadingElementMemberDoesNotReportWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/ReadingElementMemberDoesNotReportWarning.cs new file mode 100644 index 00000000000..489259b2874 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/ReadingElementMemberDoesNotReportWarning.cs @@ -0,0 +1,17 @@ +using Mirage; +using Mirage.Collections; + +public class MyClass +{ + public int Value; +} + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncList mySyncList = new SyncList(); + + public void Modify() + { + int val = mySyncList[0].Value; + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/CustomTypeWithSerializerDoesNotReportError.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/CustomTypeWithSerializerDoesNotReportError.cs new file mode 100644 index 00000000000..20025140759 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/CustomTypeWithSerializerDoesNotReportError.cs @@ -0,0 +1,19 @@ +using Mirage; +using Mirage.Serialization; + +public struct CustomType +{ + public int value; +} + +public static class CustomSerialization +{ + public static void WriteCustomType(this NetworkWriter writer, CustomType value) {} + public static CustomType ReadCustomType(this NetworkReader reader) => default; +} + +[NetworkMessage] +public struct ValidMessage +{ + public CustomType customValue; +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/MultiDimensionalArrayReportsError.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/MultiDimensionalArrayReportsError.cs new file mode 100644 index 00000000000..bcf0561821b --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/MultiDimensionalArrayReportsError.cs @@ -0,0 +1,7 @@ +using Mirage; + +[NetworkMessage] +public struct MessageWithMultiArray +{ + public int[,] {|#0:multiArray|}; +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/PrimitiveAndSupportedTypesDoNotReportError.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/PrimitiveAndSupportedTypesDoNotReportError.cs new file mode 100644 index 00000000000..1ddc1acd590 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/PrimitiveAndSupportedTypesDoNotReportError.cs @@ -0,0 +1,14 @@ +using Mirage; +using UnityEngine; +using System; + +[NetworkMessage] +public struct ValidMessage +{ + public int myInt; + public string myString; + public Vector3 myVector; + public Guid myGuid; + public DateTime myDateTime; + public byte[] myByteArray; +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/PrivateFieldsAreIgnored.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/PrivateFieldsAreIgnored.cs new file mode 100644 index 00000000000..38af9721272 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/PrivateFieldsAreIgnored.cs @@ -0,0 +1,8 @@ +using Mirage; +using System.Threading; + +[NetworkMessage] +public struct MessageWithPrivateField +{ + private Thread executionThread; +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/RecursiveTypeReportsError.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/RecursiveTypeReportsError.cs new file mode 100644 index 00000000000..26de409709d --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/RecursiveTypeReportsError.cs @@ -0,0 +1,13 @@ +using Mirage; + +[WeaverSafeClass] +public class RecursiveClass +{ + public RecursiveClass {|#0:self|}; +} + +[NetworkMessage] +public struct Message +{ + public RecursiveClass {|#1:recursiveField|}; +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/StructWithUnserializableFieldReportsError.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/StructWithUnserializableFieldReportsError.cs new file mode 100644 index 00000000000..c34c39d1658 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/StructWithUnserializableFieldReportsError.cs @@ -0,0 +1,13 @@ +using Mirage; +using System.Threading; + +public struct NestedUnserializable +{ + public Thread threadField; +} + +[NetworkMessage] +public struct MainMessage +{ + public NestedUnserializable {|#0:nestedField|}; +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/UnserializableFieldReportsError.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/UnserializableFieldReportsError.cs new file mode 100644 index 00000000000..0ff07d694db --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/UnserializableFieldReportsError.cs @@ -0,0 +1,8 @@ +using Mirage; +using System.Threading; + +[NetworkMessage] +public struct StartSessionMessage +{ + public Thread {|#0:executionThread|}; +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/UnserializablePropertyReportsError.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/UnserializablePropertyReportsError.cs new file mode 100644 index 00000000000..67e4ddf4aca --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/UnserializablePropertyReportsError.cs @@ -0,0 +1,8 @@ +using Mirage; +using System.Threading; + +[NetworkMessage] +public struct StartSessionMessage +{ + public Thread {|#0:ExecutionThread|} { get; set; } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/UnserializableRpcParameterReportsError.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/UnserializableRpcParameterReportsError.cs new file mode 100644 index 00000000000..6a3e4c00fce --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/UnserializableRpcParameterReportsError.cs @@ -0,0 +1,8 @@ +using Mirage; +using System.Threading; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc] + public void CmdStartSession(Thread {|#0:executionThread|}) {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/UnserializableRpcReturnTypeReportsError.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/UnserializableRpcReturnTypeReportsError.cs new file mode 100644 index 00000000000..0a9df6881bb --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/UnserializableRpcReturnTypeReportsError.cs @@ -0,0 +1,15 @@ +using Mirage; +using System.Threading; +using Cysharp.Threading.Tasks; + +namespace Cysharp.Threading.Tasks +{ + public struct UniTask {} + public struct UniTask {} +} + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc] + public UniTask {|#0:CmdGetSession|}() => default; +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/WeaverSafeClassWithUnserializableFieldReportsError.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/WeaverSafeClassWithUnserializableFieldReportsError.cs new file mode 100644 index 00000000000..20ecd19a4f8 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/WeaverSafeClassWithUnserializableFieldReportsError.cs @@ -0,0 +1,14 @@ +using Mirage; +using System.Threading; + +[WeaverSafeClass] +public class SafeClassWithThread +{ + public Thread threadField; +} + +[NetworkMessage] +public struct Message +{ + public SafeClassWithThread {|#0:safeClassField|}; +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/MatchingReaderAndWriterDoesNotReportError.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/MatchingReaderAndWriterDoesNotReportError.cs new file mode 100644 index 00000000000..83c598ea97b --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/MatchingReaderAndWriterDoesNotReportError.cs @@ -0,0 +1,12 @@ +using Mirage.Serialization; + +public struct CustomType +{ + public int value; +} + +public static class CustomSerialization +{ + public static void WriteCustomType(this NetworkWriter writer, CustomType value) {} + public static CustomType ReadCustomType(this NetworkReader reader) => default; +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/MismatchedArraySerializerReportsError.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/MismatchedArraySerializerReportsError.cs new file mode 100644 index 00000000000..a59a67970b8 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/MismatchedArraySerializerReportsError.cs @@ -0,0 +1,8 @@ +using Mirage.Serialization; + +public struct CustomType {} + +public static class CustomSerialization +{ + public static void {|#0:WriteCustomArray|}(this NetworkWriter writer, CustomType[] value) {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/NestedStaticClassWithMismatchedSerializerReportsError.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/NestedStaticClassWithMismatchedSerializerReportsError.cs new file mode 100644 index 00000000000..9da197956f0 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/NestedStaticClassWithMismatchedSerializerReportsError.cs @@ -0,0 +1,11 @@ +using Mirage.Serialization; + +public struct CustomType {} + +public static class OuterClass +{ + public static class InnerClass + { + public static void {|#0:WriteCustomType|}(this NetworkWriter writer, CustomType value) {} + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/NonExtensionMethodsAreIgnored.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/NonExtensionMethodsAreIgnored.cs new file mode 100644 index 00000000000..ac43e3ad857 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/NonExtensionMethodsAreIgnored.cs @@ -0,0 +1,10 @@ +using Mirage.Serialization; + +public struct CustomType {} + +public static class CustomSerialization +{ + // Not extension methods (missing 'this') + public static void WriteCustomType(NetworkWriter writer, CustomType value) {} + public static CustomType ReadCustomType(NetworkReader reader) => default; +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/ReaderOnlyReportsError.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/ReaderOnlyReportsError.cs new file mode 100644 index 00000000000..7060f1abe9e --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/ReaderOnlyReportsError.cs @@ -0,0 +1,11 @@ +using Mirage.Serialization; + +public struct CustomType +{ + public int value; +} + +public static class CustomSerialization +{ + public static CustomType {|#0:ReadCustomType|}(this NetworkReader reader) => default; +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/WriterOnlyReportsError.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/WriterOnlyReportsError.cs new file mode 100644 index 00000000000..6ad553ab2df --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/WriterOnlyReportsError.cs @@ -0,0 +1,11 @@ +using Mirage.Serialization; + +public struct CustomType +{ + public int value; +} + +public static class CustomSerialization +{ + public static void {|#0:WriteCustomType|}(this NetworkWriter writer, CustomType value) {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Edge_RecursiveStructOrClassRef.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Edge_RecursiveStructOrClassRef.cs new file mode 100644 index 00000000000..68669648905 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Edge_RecursiveStructOrClassRef.cs @@ -0,0 +1,8 @@ +using Mirage; + +[NetworkMessage] +public class RecursiveMessage +{ + public RecursiveMessage Parent; + public int Value; +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Negative_LargeMessageExceedsMtu.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Negative_LargeMessageExceedsMtu.cs new file mode 100644 index 00000000000..4f0596f7578 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Negative_LargeMessageExceedsMtu.cs @@ -0,0 +1,16 @@ +using Mirage; + +[NetworkMessage] +public struct {|#0:HugeMessage|} +{ + public double[] arr1; + public double[] arr2; + public double[] arr3; + public double[] arr4; + public double[] arr5; + public double[] arr6; + public double[] arr7; + public double[] arr8; + public double[] arr9; + public double[] arr10; +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Positive_LargeMessageReducedByPacking.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Positive_LargeMessageReducedByPacking.cs new file mode 100644 index 00000000000..d1d5e990c85 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Positive_LargeMessageReducedByPacking.cs @@ -0,0 +1,9 @@ +using Mirage; +using Mirage.Serialization; + +[NetworkMessage] +public struct PackedTestMessage +{ + [FloatPack(0.0f, 1)] // Should estimate size based on 1 bit instead of 4/8 bytes + public float CompressedVal; +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Positive_SmallMessageDoesNotTriggerWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Positive_SmallMessageDoesNotTriggerWarning.cs new file mode 100644 index 00000000000..9d5adb2bde2 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Positive_SmallMessageDoesNotTriggerWarning.cs @@ -0,0 +1,10 @@ +using Mirage; +using UnityEngine; + +[NetworkMessage] +public struct SmallMessage +{ + public int id; + public Vector3 position; + public string name; +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/OnSerializeBaseCall/Edge_BaseSyncStateFromISyncObject.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/OnSerializeBaseCall/Edge_BaseSyncStateFromISyncObject.cs new file mode 100644 index 00000000000..be9649f48e4 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/OnSerializeBaseCall/Edge_BaseSyncStateFromISyncObject.cs @@ -0,0 +1,16 @@ +using Mirage; +using Mirage.Serialization; +using Mirage.Collections; + +public class BasePlayer : NetworkBehaviour +{ + public SyncList Scores = new SyncList(); +} + +public class HeroPlayer : BasePlayer +{ + public override bool {|#0:OnSerialize|}(NetworkWriter writer, bool initialState) + { + return true; + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/OnSerializeBaseCall/Negative_MissingBaseOnDeserialize.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/OnSerializeBaseCall/Negative_MissingBaseOnDeserialize.cs new file mode 100644 index 00000000000..f3e83ebbc0c --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/OnSerializeBaseCall/Negative_MissingBaseOnDeserialize.cs @@ -0,0 +1,18 @@ +using Mirage; +using Mirage.Serialization; + +public class BasePlayer : NetworkBehaviour +{ + [SyncVar] + public string PlayerName { get; set; } +} + +public class HeroPlayer : BasePlayer +{ + [SyncVar] + public int HeroId { get; set; } + + public override void {|#0:OnDeserialize|}(NetworkReader reader, bool initialState) + { + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/OnSerializeBaseCall/Negative_MissingBaseOnSerialize.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/OnSerializeBaseCall/Negative_MissingBaseOnSerialize.cs new file mode 100644 index 00000000000..894b234729e --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/OnSerializeBaseCall/Negative_MissingBaseOnSerialize.cs @@ -0,0 +1,20 @@ +using Mirage; +using Mirage.Serialization; + +public class BasePlayer : NetworkBehaviour +{ + [SyncVar] + public string PlayerName { get; set; } +} + +public class HeroPlayer : BasePlayer +{ + [SyncVar] + public int HeroId { get; set; } + + public override bool {|#0:OnSerialize|}(NetworkWriter writer, bool initialState) + { + writer.WritePackedInt32(HeroId); + return true; + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/OnSerializeBaseCall/Positive_BaseCallIncluded.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/OnSerializeBaseCall/Positive_BaseCallIncluded.cs new file mode 100644 index 00000000000..e3ba1fdac20 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/OnSerializeBaseCall/Positive_BaseCallIncluded.cs @@ -0,0 +1,26 @@ +using Mirage; +using Mirage.Serialization; + +public class BasePlayer : NetworkBehaviour +{ + [SyncVar] + public string PlayerName { get; set; } +} + +public class HeroPlayer : BasePlayer +{ + [SyncVar] + public int HeroId { get; set; } + + public override bool OnSerialize(NetworkWriter writer, bool initialState) + { + bool baseDirty = base.OnSerialize(writer, initialState); + writer.WritePackedInt32(HeroId); + return baseDirty || true; + } + + public override void OnDeserialize(NetworkReader reader, bool initialState) + { + base.OnDeserialize(reader, initialState); + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/OnSerializeBaseCall/Positive_NoBaseSyncState.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/OnSerializeBaseCall/Positive_NoBaseSyncState.cs new file mode 100644 index 00000000000..b536af01595 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/OnSerializeBaseCall/Positive_NoBaseSyncState.cs @@ -0,0 +1,23 @@ +using Mirage; +using Mirage.Serialization; + +public class EmptyBase : NetworkBehaviour +{ + // No SyncVars or ISyncObjects here +} + +public class DerivedPlayer : EmptyBase +{ + [SyncVar] + public int HeroId { get; set; } + + public override bool OnSerialize(NetworkWriter writer, bool initialState) + { + writer.WritePackedInt32(HeroId); + return true; + } + + public override void OnDeserialize(NetworkReader reader, bool initialState) + { + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/CustomRateLimitAttributeIgnored.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/CustomRateLimitAttributeIgnored.cs new file mode 100644 index 00000000000..d321535d258 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/CustomRateLimitAttributeIgnored.cs @@ -0,0 +1,20 @@ +using Mirage; + +namespace CustomNamespace +{ + public class RateLimitAttribute : System.Attribute + { + public float Interval { get; set; } + public int Refill { get; set; } + public int MaxTokens { get; set; } + } +} + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc] + [CustomNamespace.RateLimit(Interval = -1f)] + public void {|#0:CmdFire|}() + { + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/InvalidRateLimitInterval.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/InvalidRateLimitInterval.cs new file mode 100644 index 00000000000..f306ed0f792 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/InvalidRateLimitInterval.cs @@ -0,0 +1,10 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc] + [RateLimit(Interval = 0f)] + public void {|#0:CmdFire|}() + { + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/InvalidRateLimitMaxTokensLessThanRefill.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/InvalidRateLimitMaxTokensLessThanRefill.cs new file mode 100644 index 00000000000..d03f88e579e --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/InvalidRateLimitMaxTokensLessThanRefill.cs @@ -0,0 +1,10 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc] + [RateLimit(Refill = 10, MaxTokens = 5)] + public void {|#0:CmdFire|}() + { + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/InvalidRateLimitRefillAndMaxTokens.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/InvalidRateLimitRefillAndMaxTokens.cs new file mode 100644 index 00000000000..3ab755f29a5 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/InvalidRateLimitRefillAndMaxTokens.cs @@ -0,0 +1,10 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc] + [RateLimit(Refill = -5, MaxTokens = 0)] + public void {|#0:CmdFire|}() + { + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/RateLimitOnNonRpcMethodIgnored.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/RateLimitOnNonRpcMethodIgnored.cs new file mode 100644 index 00000000000..70567d91cc0 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/RateLimitOnNonRpcMethodIgnored.cs @@ -0,0 +1,9 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [RateLimit(Interval = 0f, Refill = 0, MaxTokens = 0)] + public void LocalFire() + { + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/ValidRateLimitDefaultSettings.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/ValidRateLimitDefaultSettings.cs new file mode 100644 index 00000000000..14ac5a32531 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/ValidRateLimitDefaultSettings.cs @@ -0,0 +1,10 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc] + [RateLimit] + public void CmdFire() + { + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/ValidRateLimitSettings.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/ValidRateLimitSettings.cs new file mode 100644 index 00000000000..77498a41f90 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RateLimitSettings/ValidRateLimitSettings.cs @@ -0,0 +1,10 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc] + [RateLimit(Interval = 0.5f, Refill = 5, MaxTokens = 10)] + public void CmdFire() + { + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/InvalidClientRpcObserversWithUniTask.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/InvalidClientRpcObserversWithUniTask.cs new file mode 100644 index 00000000000..d1e4ad09a15 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/InvalidClientRpcObserversWithUniTask.cs @@ -0,0 +1,17 @@ +using Mirage; +using Cysharp.Threading.Tasks; + +namespace Cysharp.Threading.Tasks +{ + public struct UniTask {} + public struct UniTask {} +} + +public class MyBehaviour : NetworkBehaviour +{ + [ClientRpc(target = RpcTarget.Observers)] + public UniTask {|#0:RpcCalculate|}() + { + return default; + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/InvalidClientRpcPlayerWithoutConnectionParameter.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/InvalidClientRpcPlayerWithoutConnectionParameter.cs new file mode 100644 index 00000000000..f10ec0c7ef0 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/InvalidClientRpcPlayerWithoutConnectionParameter.cs @@ -0,0 +1,9 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ClientRpc(target = RpcTarget.Player)] + public void {|#0:RpcMessage|}(string msg) + { + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcObserversReturnsVoid.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcObserversReturnsVoid.cs new file mode 100644 index 00000000000..77945d5fa06 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcObserversReturnsVoid.cs @@ -0,0 +1,9 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ClientRpc] + public void RpcUpdate(int value) + { + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcOwnerWithUniTask.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcOwnerWithUniTask.cs new file mode 100644 index 00000000000..f933139f88a --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcOwnerWithUniTask.cs @@ -0,0 +1,17 @@ +using Mirage; +using Cysharp.Threading.Tasks; + +namespace Cysharp.Threading.Tasks +{ + public struct UniTask {} + public struct UniTask {} +} + +public class MyBehaviour : NetworkBehaviour +{ + [ClientRpc(target = RpcTarget.Owner)] + public UniTask RpcCalculate() + { + return default; + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcPlayerWithNetworkConnection.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcPlayerWithNetworkConnection.cs new file mode 100644 index 00000000000..76c88a9da5b --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcPlayerWithNetworkConnection.cs @@ -0,0 +1,14 @@ +using Mirage; + +namespace Mirage +{ + public class NetworkConnection {} +} + +public class MyBehaviour : NetworkBehaviour +{ + [ClientRpc(target = RpcTarget.Player)] + public void RpcMessage(NetworkConnection conn, string msg) + { + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcPlayerWithNetworkPlayer.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcPlayerWithNetworkPlayer.cs new file mode 100644 index 00000000000..3670aa2ed60 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcPlayerWithNetworkPlayer.cs @@ -0,0 +1,9 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ClientRpc(target = RpcTarget.Player)] + public void RpcMessage(INetworkPlayer player, string msg) + { + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/VerifyCS.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/VerifyCS.cs index 8f7d3afeea2..45da32f1c27 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/VerifyCS.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/VerifyCS.cs @@ -24,11 +24,20 @@ public static Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] return test.RunAsync(); } + public static string LoadTestData(string relativePath) + { + var fullPath = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "TestData", relativePath); + return System.IO.File.ReadAllText(fullPath); + } + private class Test : CSharpAnalyzerTest { public Test() { ReferenceAssemblies = ReferenceAssemblies.Default; + TestState.AdditionalReferences.Add(MetadataReference.CreateFromFile(@"d:\UnityProjects\Mirage\Library\ScriptAssemblies\Mirage.dll")); + TestState.AdditionalReferences.Add(MetadataReference.CreateFromFile(@"C:\Program Files\Unity\Hub\Editor\2022.3.62f2\Editor\Data\Managed\UnityEngine\UnityEngine.dll")); + TestState.AdditionalReferences.Add(MetadataReference.CreateFromFile(@"C:\Program Files\Unity\Hub\Editor\2022.3.62f2\Editor\Data\Managed\UnityEngine\UnityEngine.CoreModule.dll")); } } } diff --git a/CSharpAnalyzers/TestCodeImprovement.md b/CSharpAnalyzers/TestCodeImprovement.md new file mode 100644 index 00000000000..fd12240ee65 --- /dev/null +++ b/CSharpAnalyzers/TestCodeImprovement.md @@ -0,0 +1,47 @@ + +--- + +## Objective + +Migrate the C# analyzer test suite away from inline string mocks and raw text verification. Transition to an external file-based test runner that validates test cases against the real, live `Mirage` library assembly. + +--- + +## Phase 1: Establish the Real Assembly Reference + +* **Goal:** Ensure the Roslyn compiler context used during analyzer tests has access to the actual `Mirage` types. +* **Action Steps:** +* Reference the dynamically generated Unity `Mirage.csproj` (or its compiled output `.dll`) from the Analyzer Test project. +* Locate the central `VerifyCS` helper (or custom `AnalyzerTest` configuration). +* Inject the real `Mirage` metadata reference into the test environment via `MetadataReference.CreateFromFile`. + + + +--- + +## Phase 2: Implement File-Based Test Discovery + +* **Goal:** Replace raw code strings inside tests with individual, fully typed `.cs` files. +* **Action Steps:** +* Create a designated test fixtures directory (e.g., `TestData/`) inside the test project. +* Ensure any `.cs` files inside this folder are configured to copy to the output directory or treat as embedded resources. +* Refactor the test runner to load these files dynamically by name using file I/O operations (mimicking the project's existing Mono.Cecil weaver test architecture). + + + +--- + +## Phase 3: Define Test File Structure & Isolation Rules + +* **Goal:** Maintain clean separation with exactly one primary source file per test case, while allowing shared helpers if necessary. +* **Action Steps:** +* Enforce a **1 C# file per test case** rule for standard test scenarios to maximize readability and compile isolation. +* For tests requiring shared data types or common network configurations, implement a utility mechanism in the test runner to load and append shared helper snippets, or pass multiple source files into the Roslyn verifier's `TestState.Sources` collection. + + + +--- + +## Expected Outcome + +The IDE will natively provide syntax highlighting, refactoring support, and immediate compile-error feedback directly inside the test data files. Any breaking changes or API drifts in the core `Mirage` assembly will instantly fail the analyzer tests during the compilation phase, before the analyzer code even executes. \ No newline at end of file From 0367b4fbd3e6decf4c2d39deb0e920726bfa1c61 Mon Sep 17 00:00:00 2001 From: James Frowen Date: Mon, 1 Jun 2026 19:48:25 +0100 Subject: [PATCH 22/41] valid c#/mirage test code --- .../Positive_AccessInAllowedMethods.cs | 2 +- ...igningEntireElementDoesNotReportWarning.cs | 2 +- ...assWithMismatchedSerializerReportsError.cs | 2 +- .../TestDataCompilationTests.cs | 87 +++++++++++++++++++ 4 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestDataCompilationTests.cs diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Positive_AccessInAllowedMethods.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Positive_AccessInAllowedMethods.cs index da5fe308ac0..fb7126d4a46 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Positive_AccessInAllowedMethods.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Positive_AccessInAllowedMethods.cs @@ -6,7 +6,7 @@ public class ValidBehaviour : NetworkBehaviour public int Health { get; set; } [SyncVar] - public int Points; + public int Points { get; set; } public void Update() { diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/AssigningEntireElementDoesNotReportWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/AssigningEntireElementDoesNotReportWarning.cs index 7ae968f3102..0909acd848d 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/AssigningEntireElementDoesNotReportWarning.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/DirectMutation/AssigningEntireElementDoesNotReportWarning.cs @@ -2,7 +2,7 @@ using Mirage.Collections; [WeaverSafeClass] -public struct MyStruct +public class MyStruct { public int Value; } diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/NestedStaticClassWithMismatchedSerializerReportsError.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/NestedStaticClassWithMismatchedSerializerReportsError.cs index 9da197956f0..71db845fbe3 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/NestedStaticClassWithMismatchedSerializerReportsError.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MismatchedSerializer/NestedStaticClassWithMismatchedSerializerReportsError.cs @@ -2,7 +2,7 @@ public struct CustomType {} -public static class OuterClass +namespace OuterNamespace { public static class InnerClass { diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestDataCompilationTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestDataCompilationTests.cs new file mode 100644 index 00000000000..a3b8a376db5 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestDataCompilationTests.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using NUnit.Framework; + +namespace Mirage.Analyzers.Tests +{ + [TestFixture] + public class TestDataCompilationTests + { + private static readonly Regex MarkupRegex = new Regex(@"\{\|[^:]+:(.*?)\|\}", RegexOptions.Compiled); + + private static IEnumerable GetTestDataFiles() + { + var testDataDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestData"); + if (!Directory.Exists(testDataDir)) + yield break; + + var files = Directory.GetFiles(testDataDir, "*.cs", SearchOption.AllDirectories); + foreach (var file in files) + yield return file; + } + + [Test] + [TestCaseSource(nameof(GetTestDataFiles))] + public void TestCompilation(string filePath) + { + var content = File.ReadAllText(filePath); + var cleanedContent = StripMarkup(content); + + var syntaxTree = CSharpSyntaxTree.ParseText(cleanedContent); + + var paths = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + if (!assembly.IsDynamic && !string.IsNullOrEmpty(assembly.Location)) + paths.Add(assembly.Location); + } + + paths.Add(@"d:\UnityProjects\Mirage\Library\ScriptAssemblies\Mirage.dll"); + paths.Add(@"C:\Program Files\Unity\Hub\Editor\2022.3.62f2\Editor\Data\Managed\UnityEngine\UnityEngine.dll"); + paths.Add(@"C:\Program Files\Unity\Hub\Editor\2022.3.62f2\Editor\Data\Managed\UnityEngine\UnityEngine.CoreModule.dll"); + + var references = new List(); + foreach (var path in paths) + { + if (File.Exists(path)) + references.Add(MetadataReference.CreateFromFile(path)); + } + + var compilation = CSharpCompilation.Create( + assemblyName: "TestCompilation_" + Path.GetFileNameWithoutExtension(filePath), + syntaxTrees: new[] { syntaxTree }, + references: references, + options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); + + var diagnostics = compilation.GetDiagnostics(); + var errors = new List(); + foreach (var diag in diagnostics) + { + if (diag.Severity == DiagnosticSeverity.Error) + errors.Add(diag); + } + + if (errors.Count > 0) + { + var sb = new System.Text.StringBuilder(); + sb.AppendLine($"Compilation failed for file: {filePath}"); + foreach (var err in errors) + sb.AppendLine(err.ToString()); + Assert.Fail(sb.ToString()); + } + } + + private static string StripMarkup(string content) + { + var cleaned = content; + while (MarkupRegex.IsMatch(cleaned)) + cleaned = MarkupRegex.Replace(cleaned, "$1"); + return cleaned; + } + } +} From b567505c89852c671ce480ac5a672ebe267722e0 Mon Sep 17 00:00:00 2001 From: James Frowen Date: Mon, 1 Jun 2026 20:06:19 +0100 Subject: [PATCH 23/41] fixed tests --- .../FieldSerializationTests.cs | 33 ++++++ .../MisplacedNetworkAttributeTests.cs | 59 ++++++++++ .../RpcSignatureTests.cs | 50 ++++++++ .../SyncVarAutoPropertyTests.cs | 48 ++++++++ .../SyncVarClassTests.cs | 111 ++---------------- .../Attributes/ClientOnNonNetworkBehaviour.cs | 9 ++ .../ClientRpcOnNonNetworkBehaviour.cs | 9 ++ .../Attributes/ServerOnNonNetworkBehaviour.cs | 9 ++ .../ServerRpcOnNonNetworkBehaviour.cs | 9 ++ .../SyncVarOnNonNetworkBehaviour.cs | 7 ++ ...rSafeClassOnFieldSuppressesClassWarning.cs | 9 ++ ...eClassOnParameterSuppressesClassWarning.cs | 10 ++ ...feClassOnPropertySuppressesClassWarning.cs | 9 ++ .../TestData/Rpcs/GenericRpc.cs | 9 ++ .../TestData/Rpcs/RpcWithInvalidReturnType.cs | 10 ++ .../TestData/Rpcs/ValidGenericUniTaskRpc.cs | 17 +++ .../TestData/Rpcs/ValidUniTaskRpc.cs | 17 +++ .../TestData/Rpcs/ValidVoidRpc.cs | 9 ++ .../SyncVars/ClassLevelSuppression.cs | 10 ++ .../SyncVars/ClassTypeReportsWarning.cs | 9 ++ .../SyncVars/MissingGetterProperty.cs | 7 ++ .../SyncVars/MissingSetterProperty.cs | 7 ++ .../TestData/SyncVars/NonAutoProperty.cs | 13 ++ .../SyncVars/PropertyLevelSuppression.cs | 10 ++ .../SyncVars/SafeTypeDoesNotReportWarning.cs | 7 ++ .../TestData/SyncVars/StaticProperty.cs | 7 ++ .../TestData/SyncVars/ValidAutoProperty.cs | 7 ++ 27 files changed, 409 insertions(+), 102 deletions(-) create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/MisplacedNetworkAttributeTests.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/RpcSignatureTests.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/SyncVarAutoPropertyTests.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Attributes/ClientOnNonNetworkBehaviour.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Attributes/ClientRpcOnNonNetworkBehaviour.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Attributes/ServerOnNonNetworkBehaviour.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Attributes/ServerRpcOnNonNetworkBehaviour.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Attributes/SyncVarOnNonNetworkBehaviour.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/WeaverSafeClassOnFieldSuppressesClassWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/WeaverSafeClassOnParameterSuppressesClassWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/WeaverSafeClassOnPropertySuppressesClassWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Rpcs/GenericRpc.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Rpcs/RpcWithInvalidReturnType.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Rpcs/ValidGenericUniTaskRpc.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Rpcs/ValidUniTaskRpc.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Rpcs/ValidVoidRpc.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/ClassLevelSuppression.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/ClassTypeReportsWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/MissingGetterProperty.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/MissingSetterProperty.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/NonAutoProperty.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/PropertyLevelSuppression.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/SafeTypeDoesNotReportWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/StaticProperty.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/ValidAutoProperty.cs diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/FieldSerializationTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/FieldSerializationTests.cs index ce1bcc1f756..18be82d88ca 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/FieldSerializationTests.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/FieldSerializationTests.cs @@ -134,5 +134,38 @@ public async Task WeaverSafeClassWithUnserializableFieldReportsError() await VerifyCS.VerifyAnalyzerAsync(code, expected); } + + [Test] + public async Task WeaverSafeClassOnFieldSuppressesClassWarning() + { + var code = VerifyCS.LoadTestData("FieldSerialization/WeaverSafeClassOnFieldSuppressesClassWarning.cs"); + var expected = VerifyCS.Diagnostic("MIRAGE1302") + .WithLocation(0) + .WithArguments("Thread", "NetworkMessage field"); + + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task WeaverSafeClassOnPropertySuppressesClassWarning() + { + var code = VerifyCS.LoadTestData("FieldSerialization/WeaverSafeClassOnPropertySuppressesClassWarning.cs"); + var expected = VerifyCS.Diagnostic("MIRAGE1302") + .WithLocation(0) + .WithArguments("Thread", "NetworkMessage property"); + + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task WeaverSafeClassOnParameterSuppressesClassWarning() + { + var code = VerifyCS.LoadTestData("FieldSerialization/WeaverSafeClassOnParameterSuppressesClassWarning.cs"); + var expected = VerifyCS.Diagnostic("MIRAGE1302") + .WithLocation(0) + .WithArguments("Thread", "RPC parameter"); + + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } } } diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/MisplacedNetworkAttributeTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/MisplacedNetworkAttributeTests.cs new file mode 100644 index 00000000000..4bd006ed7b9 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/MisplacedNetworkAttributeTests.cs @@ -0,0 +1,59 @@ +using NUnit.Framework; +using System.Threading.Tasks; + +namespace Mirage.Analyzers.Tests +{ + [TestFixture] + public class MisplacedNetworkAttributeTests + { + [Test] + public async Task SyncVarOnNonNetworkBehaviourReportsError() + { + var code = VerifyCS.LoadTestData("Attributes/SyncVarOnNonNetworkBehaviour.cs"); + var expected = VerifyCS.Diagnostic("MIRAGE1101") + .WithLocation(0) + .WithArguments("SyncVarAttribute", "MySyncVar"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task ServerOnNonNetworkBehaviourReportsError() + { + var code = VerifyCS.LoadTestData("Attributes/ServerOnNonNetworkBehaviour.cs"); + var expected = VerifyCS.Diagnostic("MIRAGE1101") + .WithLocation(0) + .WithArguments("ServerAttribute", "MyMethod"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task ClientOnNonNetworkBehaviourReportsError() + { + var code = VerifyCS.LoadTestData("Attributes/ClientOnNonNetworkBehaviour.cs"); + var expected = VerifyCS.Diagnostic("MIRAGE1101") + .WithLocation(0) + .WithArguments("ClientAttribute", "MyMethod"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task ServerRpcOnNonNetworkBehaviourReportsError() + { + var code = VerifyCS.LoadTestData("Attributes/ServerRpcOnNonNetworkBehaviour.cs"); + var expected = VerifyCS.Diagnostic("MIRAGE1101") + .WithLocation(0) + .WithArguments("ServerRpcAttribute", "MyMethod"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task ClientRpcOnNonNetworkBehaviourReportsError() + { + var code = VerifyCS.LoadTestData("Attributes/ClientRpcOnNonNetworkBehaviour.cs"); + var expected = VerifyCS.Diagnostic("MIRAGE1101") + .WithLocation(0) + .WithArguments("ClientRpcAttribute", "MyMethod"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/RpcSignatureTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/RpcSignatureTests.cs new file mode 100644 index 00000000000..75407d35172 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/RpcSignatureTests.cs @@ -0,0 +1,50 @@ +using NUnit.Framework; +using System.Threading.Tasks; + +namespace Mirage.Analyzers.Tests +{ + [TestFixture] + public class RpcSignatureTests + { + [Test] + public async Task GenericRpcReportsError() + { + var code = VerifyCS.LoadTestData("Rpcs/GenericRpc.cs"); + var expected = VerifyCS.Diagnostic("MIRAGE1201") + .WithLocation(0) + .WithArguments("CmdGeneric", "cannot have generic parameters"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task RpcWithInvalidReturnTypeReportsError() + { + var code = VerifyCS.LoadTestData("Rpcs/RpcWithInvalidReturnType.cs"); + var expected = VerifyCS.Diagnostic("MIRAGE1201") + .WithLocation(0) + .WithArguments("CmdReturnsInt", "cannot return 'int' (must return void or UniTask)"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task ValidVoidRpcDoesNotReportError() + { + var code = VerifyCS.LoadTestData("Rpcs/ValidVoidRpc.cs"); + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task ValidUniTaskRpcDoesNotReportError() + { + var code = VerifyCS.LoadTestData("Rpcs/ValidUniTaskRpc.cs"); + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task ValidGenericUniTaskRpcDoesNotReportError() + { + var code = VerifyCS.LoadTestData("Rpcs/ValidGenericUniTaskRpc.cs"); + await VerifyCS.VerifyAnalyzerAsync(code); + } + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/SyncVarAutoPropertyTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/SyncVarAutoPropertyTests.cs new file mode 100644 index 00000000000..055d8ba1386 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/SyncVarAutoPropertyTests.cs @@ -0,0 +1,48 @@ +using NUnit.Framework; +using System.Threading.Tasks; + +namespace Mirage.Analyzers.Tests +{ + [TestFixture] + public class SyncVarAutoPropertyTests + { + [Test] + public async Task ValidAutoPropertyDoesNotReportError() + { + var code = VerifyCS.LoadTestData("SyncVars/ValidAutoProperty.cs"); + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Test] + public async Task NonAutoPropertyReportsError() + { + var code = VerifyCS.LoadTestData("SyncVars/NonAutoProperty.cs"); + var expected = VerifyCS.Diagnostic("MIRAGE1002").WithLocation(0).WithArguments("MySyncVar"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task StaticPropertyReportsError() + { + var code = VerifyCS.LoadTestData("SyncVars/StaticProperty.cs"); + var expected = VerifyCS.Diagnostic("MIRAGE1002").WithLocation(0).WithArguments("MySyncVar"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task MissingSetterPropertyReportsError() + { + var code = VerifyCS.LoadTestData("SyncVars/MissingSetterProperty.cs"); + var expected = VerifyCS.Diagnostic("MIRAGE1002").WithLocation(0).WithArguments("MySyncVar"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + + [Test] + public async Task MissingGetterPropertyReportsError() + { + var code = VerifyCS.LoadTestData("SyncVars/MissingGetterProperty.cs"); + var expected = VerifyCS.Diagnostic("MIRAGE1002").WithLocation(0).WithArguments("MySyncVar"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); + } + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/SyncVarClassTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/SyncVarClassTests.cs index 8a0062182f1..cca1f5b4023 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/SyncVarClassTests.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/SyncVarClassTests.cs @@ -6,126 +6,33 @@ namespace Mirage.Analyzers.Tests [TestFixture] public class SyncVarClassTests { - private const string MockDefinitions = @" -namespace Mirage -{ - public class NetworkBehaviour {} - public class SyncVarAttribute : System.Attribute {} - public class WeaverSafeClassAttribute : System.Attribute {} -} -namespace Mirage.Collections -{ - public interface ISyncObject {} - public class SyncList : ISyncObject - { - public T this[int index] { get => default; set {} } - } -} -"; - [Test] public async Task SafeTypeDoesNotReportWarning() { - // Verify that primitive type syncvars do not trigger MIRAGE1001 warning - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - [SyncVar] - public int mySyncVar; -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("SyncVars/SafeTypeDoesNotReportWarning.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task ClassTypeReportsWarning() { - // Verify that class type syncvars trigger MIRAGE1001 warning with the correct argument description - var code = @" -using Mirage; - -public class MyClass {} - -public class MyBehaviour : NetworkBehaviour -{ - [SyncVar] - public MyClass {|#0:mySyncVar|}; -} -" + MockDefinitions; - - var expected = VerifyCS.Diagnostic("MIRAGE1001").WithLocation(0).WithArguments("mySyncVar", "MyClass"); + var code = VerifyCS.LoadTestData("SyncVars/ClassTypeReportsWarning.cs"); + var expected = VerifyCS.Diagnostic("MIRAGE1001").WithLocation(0).WithArguments("MySyncVar", "MyClass"); await VerifyCS.VerifyAnalyzerAsync(code, expected); } [Test] - public async Task SyncObjectNotReadonlyReportsError() + public async Task ClassLevelSuppressionDoesNotReportWarning() { - var code = @" -using Mirage; -using Mirage.Collections; - -public class MyBehaviour : NetworkBehaviour -{ - public SyncList {|#0:mySyncList|}; -} -" + MockDefinitions; - - var expected = VerifyCS.Diagnostic("MIRAGE1004").WithLocation(0).WithArguments("mySyncList"); - await VerifyCS.VerifyAnalyzerAsync(code, expected); - } - - [Test] - public async Task SyncObjectReassignmentReportsError() - { - var code = @" -using Mirage; -using Mirage.Collections; - -public class MyBehaviour : NetworkBehaviour -{ - public SyncList {|#0:mySyncList|} = new SyncList(); - - public void Modify() - { - {|#1:mySyncList|} = new SyncList(); - } -} -" + MockDefinitions; - - var expectedField = VerifyCS.Diagnostic("MIRAGE1004").WithLocation(0).WithArguments("mySyncList"); - var expectedAssignment = VerifyCS.Diagnostic("MIRAGE1004").WithLocation(1).WithArguments("mySyncList"); - await VerifyCS.VerifyAnalyzerAsync(code, expectedField, expectedAssignment); + var code = VerifyCS.LoadTestData("SyncVars/ClassLevelSuppression.cs"); + await VerifyCS.VerifyAnalyzerAsync(code); } [Test] - public async Task DirectMutationOfSyncListElementReportsWarning() + public async Task PropertyLevelSuppressionDoesNotReportWarning() { - var code = @" -using Mirage; -using Mirage.Collections; - -[WeaverSafeClass] -public class MyClass -{ - public int Value; -} - -public class MyBehaviour : NetworkBehaviour -{ - public readonly SyncList mySyncList = new SyncList(); - - public void Modify() - { - {|#0:mySyncList[0]|}.Value = 10; - } -} -" + MockDefinitions; - - var expected = VerifyCS.Diagnostic("MIRAGE1003").WithLocation(0).WithArguments("mySyncList"); - await VerifyCS.VerifyAnalyzerAsync(code, expected); + var code = VerifyCS.LoadTestData("SyncVars/PropertyLevelSuppression.cs"); + await VerifyCS.VerifyAnalyzerAsync(code); } } } diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Attributes/ClientOnNonNetworkBehaviour.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Attributes/ClientOnNonNetworkBehaviour.cs new file mode 100644 index 00000000000..832fa726b3d --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Attributes/ClientOnNonNetworkBehaviour.cs @@ -0,0 +1,9 @@ +using Mirage; + +public class NonBehaviour +{ + [{|#0:Client|}] + public void MyMethod() + { + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Attributes/ClientRpcOnNonNetworkBehaviour.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Attributes/ClientRpcOnNonNetworkBehaviour.cs new file mode 100644 index 00000000000..77633f1e314 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Attributes/ClientRpcOnNonNetworkBehaviour.cs @@ -0,0 +1,9 @@ +using Mirage; + +public class NonBehaviour +{ + [{|#0:ClientRpc|}] + public void MyMethod() + { + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Attributes/ServerOnNonNetworkBehaviour.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Attributes/ServerOnNonNetworkBehaviour.cs new file mode 100644 index 00000000000..1c355f6a7db --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Attributes/ServerOnNonNetworkBehaviour.cs @@ -0,0 +1,9 @@ +using Mirage; + +public class NonBehaviour +{ + [{|#0:Server|}] + public void MyMethod() + { + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Attributes/ServerRpcOnNonNetworkBehaviour.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Attributes/ServerRpcOnNonNetworkBehaviour.cs new file mode 100644 index 00000000000..5f6704d1d41 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Attributes/ServerRpcOnNonNetworkBehaviour.cs @@ -0,0 +1,9 @@ +using Mirage; + +public class NonBehaviour +{ + [{|#0:ServerRpc|}] + public void MyMethod() + { + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Attributes/SyncVarOnNonNetworkBehaviour.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Attributes/SyncVarOnNonNetworkBehaviour.cs new file mode 100644 index 00000000000..dad178eff9b --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Attributes/SyncVarOnNonNetworkBehaviour.cs @@ -0,0 +1,7 @@ +using Mirage; + +public class NonBehaviour +{ + [{|#0:SyncVar|}] + public int MySyncVar { get; set; } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/WeaverSafeClassOnFieldSuppressesClassWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/WeaverSafeClassOnFieldSuppressesClassWarning.cs new file mode 100644 index 00000000000..8a984e006ea --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/WeaverSafeClassOnFieldSuppressesClassWarning.cs @@ -0,0 +1,9 @@ +using Mirage; +using System.Threading; + +[NetworkMessage] +public struct StartSessionMessage +{ + [WeaverSafeClass] + public Thread {|#0:executionThread|}; +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/WeaverSafeClassOnParameterSuppressesClassWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/WeaverSafeClassOnParameterSuppressesClassWarning.cs new file mode 100644 index 00000000000..905cb1eda78 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/WeaverSafeClassOnParameterSuppressesClassWarning.cs @@ -0,0 +1,10 @@ +using Mirage; +using System.Threading; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc, RateLimit] + public void CmdStartSession([WeaverSafeClass] Thread {|#0:executionThread|}) + { + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/WeaverSafeClassOnPropertySuppressesClassWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/WeaverSafeClassOnPropertySuppressesClassWarning.cs new file mode 100644 index 00000000000..209b84248bb --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/FieldSerialization/WeaverSafeClassOnPropertySuppressesClassWarning.cs @@ -0,0 +1,9 @@ +using Mirage; +using System.Threading; + +[NetworkMessage] +public struct StartSessionMessage +{ + [WeaverSafeClass] + public Thread {|#0:ExecutionThread|} { get; set; } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Rpcs/GenericRpc.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Rpcs/GenericRpc.cs new file mode 100644 index 00000000000..5e6c423f3c0 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Rpcs/GenericRpc.cs @@ -0,0 +1,9 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc, RateLimit] + public void {|#0:CmdGeneric|}(T val) + { + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Rpcs/RpcWithInvalidReturnType.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Rpcs/RpcWithInvalidReturnType.cs new file mode 100644 index 00000000000..7359616aa99 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Rpcs/RpcWithInvalidReturnType.cs @@ -0,0 +1,10 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc, RateLimit] + public int {|#0:CmdReturnsInt|}() + { + return 0; + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Rpcs/ValidGenericUniTaskRpc.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Rpcs/ValidGenericUniTaskRpc.cs new file mode 100644 index 00000000000..4daa6dd60a8 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Rpcs/ValidGenericUniTaskRpc.cs @@ -0,0 +1,17 @@ +using Mirage; +using Cysharp.Threading.Tasks; + +namespace Cysharp.Threading.Tasks +{ + public struct UniTask {} + public struct UniTask {} +} + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc, RateLimit] + public UniTask CmdReturnsGenericUniTask() + { + return default; + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Rpcs/ValidUniTaskRpc.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Rpcs/ValidUniTaskRpc.cs new file mode 100644 index 00000000000..a9e8e1e2cad --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Rpcs/ValidUniTaskRpc.cs @@ -0,0 +1,17 @@ +using Mirage; +using Cysharp.Threading.Tasks; + +namespace Cysharp.Threading.Tasks +{ + public struct UniTask {} + public struct UniTask {} +} + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc, RateLimit] + public UniTask CmdReturnsUniTask() + { + return default; + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Rpcs/ValidVoidRpc.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Rpcs/ValidVoidRpc.cs new file mode 100644 index 00000000000..a36137973ae --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/Rpcs/ValidVoidRpc.cs @@ -0,0 +1,9 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc, RateLimit] + public void CmdReturnsVoid() + { + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/ClassLevelSuppression.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/ClassLevelSuppression.cs new file mode 100644 index 00000000000..d565fec6425 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/ClassLevelSuppression.cs @@ -0,0 +1,10 @@ +using Mirage; + +[WeaverSafeClass] +public class MyClass {} + +public class MyBehaviour : NetworkBehaviour +{ + [SyncVar] + public MyClass MySyncVar { get; set; } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/ClassTypeReportsWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/ClassTypeReportsWarning.cs new file mode 100644 index 00000000000..8bc169114f3 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/ClassTypeReportsWarning.cs @@ -0,0 +1,9 @@ +using Mirage; + +public class MyClass {} + +public class MyBehaviour : NetworkBehaviour +{ + [SyncVar] + public MyClass {|#0:MySyncVar|} { get; set; } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/MissingGetterProperty.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/MissingGetterProperty.cs new file mode 100644 index 00000000000..287e4509ffc --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/MissingGetterProperty.cs @@ -0,0 +1,7 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [SyncVar] + public int {|#0:MySyncVar|} { set {} } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/MissingSetterProperty.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/MissingSetterProperty.cs new file mode 100644 index 00000000000..4e6839dfcc9 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/MissingSetterProperty.cs @@ -0,0 +1,7 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [SyncVar] + public int {|#0:MySyncVar|} { get; } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/NonAutoProperty.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/NonAutoProperty.cs new file mode 100644 index 00000000000..f2149170136 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/NonAutoProperty.cs @@ -0,0 +1,13 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + private int _mySyncVar; + + [SyncVar] + public int {|#0:MySyncVar|} + { + get => _mySyncVar; + set => _mySyncVar = value; + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/PropertyLevelSuppression.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/PropertyLevelSuppression.cs new file mode 100644 index 00000000000..43576ff805b --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/PropertyLevelSuppression.cs @@ -0,0 +1,10 @@ +using Mirage; + +public class MyClass {} + +public class MyBehaviour : NetworkBehaviour +{ + [SyncVar] + [WeaverSafeClass] + public MyClass MySyncVar { get; set; } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/SafeTypeDoesNotReportWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/SafeTypeDoesNotReportWarning.cs new file mode 100644 index 00000000000..0506b691239 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/SafeTypeDoesNotReportWarning.cs @@ -0,0 +1,7 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [SyncVar] + public int MySyncVar { get; set; } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/StaticProperty.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/StaticProperty.cs new file mode 100644 index 00000000000..aff11d6a00d --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/StaticProperty.cs @@ -0,0 +1,7 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [SyncVar] + public static int {|#0:MySyncVar|} { get; set; } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/ValidAutoProperty.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/ValidAutoProperty.cs new file mode 100644 index 00000000000..0506b691239 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncVars/ValidAutoProperty.cs @@ -0,0 +1,7 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [SyncVar] + public int MySyncVar { get; set; } +} From 0b25b2150fbc11ee1bb0398552fb3574defd8eed Mon Sep 17 00:00:00 2001 From: James Frowen Date: Mon, 1 Jun 2026 22:24:19 +0100 Subject: [PATCH 24/41] moving docs code to snippets --- .../Mirage/Samples~/Snippets/Analyzers.meta | 8 + .../Samples~/Snippets/Analyzers/Mirage1001.cs | 69 +++++ .../Snippets/Analyzers/Mirage1001.cs.meta | 11 + .../Samples~/Snippets/Analyzers/Mirage1002.cs | 33 ++ .../Snippets/Analyzers/Mirage1002.cs.meta | 11 + .../Samples~/Snippets/Analyzers/Mirage1003.cs | 49 +++ .../Snippets/Analyzers/Mirage1003.cs.meta | 11 + .../Samples~/Snippets/Analyzers/Mirage1004.cs | 38 +++ .../Snippets/Analyzers/Mirage1004.cs.meta | 11 + .../Samples~/Snippets/Analyzers/Mirage1101.cs | 28 ++ .../Snippets/Analyzers/Mirage1101.cs.meta | 11 + .../Samples~/Snippets/Analyzers/Mirage1201.cs | 48 +++ .../Snippets/Analyzers/Mirage1201.cs.meta | 11 + .../Samples~/Snippets/Analyzers/Mirage1202.cs | 37 +++ .../Snippets/Analyzers/Mirage1202.cs.meta | 11 + .../Samples~/Snippets/Analyzers/Mirage1203.cs | 34 ++ .../Snippets/Analyzers/Mirage1203.cs.meta | 11 + .../Samples~/Snippets/Analyzers/Mirage1204.cs | 47 +++ .../Snippets/Analyzers/Mirage1204.cs.meta | 11 + .../Samples~/Snippets/Analyzers/Mirage1205.cs | 34 ++ .../Snippets/Analyzers/Mirage1205.cs.meta | 11 + .../Samples~/Snippets/Analyzers/Mirage1206.cs | 33 ++ .../Snippets/Analyzers/Mirage1206.cs.meta | 11 + .../Samples~/Snippets/Analyzers/Mirage1301.cs | 69 +++++ .../Snippets/Analyzers/Mirage1301.cs.meta | 11 + .../Samples~/Snippets/Analyzers/Mirage1302.cs | 29 ++ .../Snippets/Analyzers/Mirage1302.cs.meta | 11 + .../Samples~/Snippets/Analyzers/Mirage1303.cs | 48 +++ .../Snippets/Analyzers/Mirage1303.cs.meta | 11 + .../Samples~/Snippets/Analyzers/Mirage1401.cs | 42 +++ .../Snippets/Analyzers/Mirage1401.cs.meta | 11 + .../Samples~/Snippets/Analyzers/Mirage1402.cs | 54 ++++ .../Snippets/Analyzers/Mirage1402.cs.meta | 11 + .../Samples~/Snippets/Analyzers/Mirage1501.cs | 29 ++ .../Snippets/Analyzers/Mirage1501.cs.meta | 11 + .../Samples~/Snippets/Analyzers/Mirage1502.cs | 30 ++ .../Snippets/Analyzers/Mirage1502.cs.meta | 11 + .../Samples~/Snippets/Analyzers/Mirage1503.cs | 37 +++ .../Snippets/Analyzers/Mirage1503.cs.meta | 11 + .../Samples~/Snippets/Authentication.meta | 8 + .../BasicAuthenticatorSnippets.cs | 11 + .../BasicAuthenticatorSnippets.cs.meta | 11 + .../CustomAuthenticator.cs | 0 .../CustomAuthenticator.cs.meta | 2 +- .../Mirage/Samples~/Snippets/BitPacking.meta | 8 + .../BitPacking/BitCountFromRangeSnippets.cs | 86 ++++++ .../BitCountFromRangeSnippets.cs.meta | 11 + .../Snippets/BitPacking/BitCountSnippets.cs | 75 +++++ .../BitPacking/BitCountSnippets.cs.meta | 11 + .../Snippets/BitPacking/FloatPackSnippets.cs | 77 +++++ .../BitPacking/FloatPackSnippets.cs.meta | 11 + .../BitPacking/QuaternionPackSnippets.cs | 65 ++++ .../BitPacking/QuaternionPackSnippets.cs.meta | 11 + .../BitPacking/VarIntBlocksSnippets.cs | 80 +++++ .../BitPacking/VarIntBlocksSnippets.cs.meta | 11 + .../Snippets/BitPacking/VectorPackSnippets.cs | 100 ++++++ .../BitPacking/VectorPackSnippets.cs.meta | 11 + .../BitPacking/ZigZagEncodeSnippets.cs | 62 ++++ .../BitPacking/ZigZagEncodeSnippets.cs.meta | 11 + .../Mirage/Samples~/Snippets/Callbacks.meta | 8 + .../Callbacks/NetworkBehaviourCallbacks.cs | 31 ++ .../NetworkBehaviourCallbacks.cs.meta | 11 + .../Samples~/Snippets/CommunityGuides.meta | 8 + .../CommunityGuides/QuickStartGuide.cs | 292 ++++++++++++++++++ .../CommunityGuides/QuickStartGuide.cs.meta | 11 + .../Mirage/Samples~/Snippets/Components.meta | 8 + .../{ => Components}/LobbyReadyCheck.cs | 2 +- .../{ => Components}/LobbyReadyCheck.cs.meta | 2 +- .../Components/NetworkDiscoverySnippet.cs | 73 +++++ .../NetworkDiscoverySnippet.cs.meta | 11 + .../Components/NetworkLogSettingsSnippet.cs | 45 +++ .../NetworkLogSettingsSnippet.cs.meta | 11 + .../Components/NetworkSceneCheckerSnippet.cs | 60 ++++ .../NetworkSceneCheckerSnippet.cs.meta | 11 + .../Components/NetworkSceneManagerSnippet.cs | 24 ++ .../NetworkSceneManagerSnippet.cs.meta | 11 + .../Mirage/Samples~/Snippets/GameObjects.meta | 8 + .../GameObjects/CustomPlayerSpawning.cs | 163 ++++++++++ .../GameObjects/CustomPlayerSpawning.cs.meta | 11 + .../GameObjects/CustomSpawnExample.cs | 104 +++++++ .../GameObjects/CustomSpawnExample.cs.meta | 11 + .../GameObjects/LifecycleComponent.cs | 19 ++ .../GameObjects/LifecycleComponent.cs.meta | 11 + .../Snippets/GameObjects/MyNetworkManager.cs | 70 +++++ .../GameObjects/MyNetworkManager.cs.meta | 11 + .../GameObjects/NetworkWorldEvents.cs | 52 ++++ .../GameObjects/NetworkWorldEvents.cs.meta | 11 + .../Snippets/GameObjects/PlayerCharacter.cs | 18 ++ .../GameObjects/PlayerCharacter.cs.meta | 11 + .../Snippets/GameObjects/PlayerContext.cs | 15 + .../GameObjects/PlayerContext.cs.meta | 11 + .../Snippets/GameObjects/PlayerEquip.cs | 123 ++++++++ .../Snippets/GameObjects/PlayerEquip.cs.meta | 11 + .../GameObjects/PlayerEquipInitial.cs | 79 +++++ .../GameObjects/PlayerEquipInitial.cs.meta | 11 + .../GameObjects/PlayerProxyManager.cs | 48 +++ .../GameObjects/PlayerProxyManager.cs.meta | 11 + .../Snippets/GameObjects/SceneObject.cs | 64 ++++ .../Snippets/GameObjects/SceneObject.cs.meta | 11 + .../GameObjects/SceneObjectFilterExample.cs | 37 +++ .../SceneObjectFilterExample.cs.meta | 11 + .../Snippets/GameObjects/SpawningExample.cs | 50 +++ .../GameObjects/SpawningExample.cs.meta | 11 + .../Samples~/Snippets/GameObjects/Tree.cs | 55 ++++ .../Snippets/GameObjects/Tree.cs.meta | 11 + Assets/Mirage/Samples~/Snippets/General.meta | 8 + .../Snippets/General/AttributesSnippets.cs | 25 ++ .../General/AttributesSnippets.cs.meta | 11 + .../Snippets/General/AuthoritySnippets.cs | 43 +++ .../General/AuthoritySnippets.cs.meta | 11 + .../Snippets/General/BestPracticesSnippets.cs | 13 + .../General/BestPracticesSnippets.cs.meta | 11 + .../Snippets/General/ClockSyncSnippets.cs | 42 +++ .../General/ClockSyncSnippets.cs.meta | 11 + .../Snippets/General/ErrorHandlingSnippets.cs | 187 +++++++++++ .../General/ErrorHandlingSnippets.cs.meta | 11 + .../General/GettingStartedSnippets.cs | 40 +++ .../General/GettingStartedSnippets.cs.meta | 11 + .../General/TroubleshootingSnippets.cs | 30 ++ .../General/TroubleshootingSnippets.cs.meta | 11 + Assets/Mirage/Samples~/Snippets/Guides.meta | 8 + .../Samples~/Snippets/Guides/FaqSnippets.cs | 22 ++ .../Snippets/Guides/FaqSnippets.cs.meta | 11 + .../Mirage/Samples~/Snippets/Messaging.meta | 8 + .../{ => Messaging}/SendNetworkMessage.cs | 2 +- .../SendNetworkMessage.cs.meta | 2 +- .../Samples~/Snippets/RemoteActions.meta | 8 + .../RemoteActions/ClientRpcExamples.cs | 95 ++++++ .../RemoteActions/ClientRpcExamples.cs.meta | 11 + .../RemoteActions/RateLimitingExamples.cs | 36 +++ .../RateLimitingExamples.cs.meta | 11 + .../RemoteActions/RpcExampleChangeName.cs | 18 ++ .../RpcExampleChangeName.cs.meta | 11 + .../RpcExampleChangeNameGenerated.cs | 71 +++++ .../RpcExampleChangeNameGenerated.cs.meta | 11 + .../Snippets/RemoteActions/ServerRpcDoor.cs | 36 +++ .../RemoteActions/ServerRpcDoor.cs.meta | 11 + .../RemoteActions/ServerRpcDropCube.cs | 40 +++ .../RemoteActions/ServerRpcDropCube.cs.meta | 11 + Assets/Mirage/Samples~/Snippets/Rpc.meta | 8 + .../Samples~/Snippets/{ => Rpc}/RpcReply.cs | 2 +- .../Snippets/{ => Rpc}/RpcReply.cs.meta | 2 +- .../Samples~/Snippets/SceneLoading.meta | 8 + .../SceneLoading/CustomSceneManager.cs | 57 ++++ .../SceneLoading/CustomSceneManager.cs.meta | 11 + .../Snippets/SceneLoading/FixedMatchSize.cs | 38 +++ .../SceneLoading/FixedMatchSize.cs.meta | 11 + .../SceneLoading/LoadSceneAdditively.cs | 19 ++ .../SceneLoading/LoadSceneAdditively.cs.meta | 11 + .../SceneLoading/LoadSceneAdditivelyPlayer.cs | 29 ++ .../LoadSceneAdditivelyPlayer.cs.meta | 11 + .../Snippets/SceneLoading/LoadSceneNormal.cs | 17 + .../SceneLoading/LoadSceneNormal.cs.meta | 11 + .../SceneLoading/LoadSceneNormalParams.cs | 16 + .../LoadSceneNormalParams.cs.meta | 11 + .../Snippets/SceneLoading/LoaderUsage.cs | 19 ++ .../Snippets/SceneLoading/LoaderUsage.cs.meta | 11 + .../SceneLoading/UnLoadSceneAdditively.cs | 21 ++ .../UnLoadSceneAdditively.cs.meta | 11 + .../Samples~/Snippets/Serialization.meta | 8 + .../Snippets/Serialization/CustomReadWrite.cs | 25 ++ .../Serialization/CustomReadWrite.cs.meta | 11 + .../Serialization/DataTypesSnippets.cs | 183 +++++++++++ .../Serialization/DataTypesSnippets.cs.meta | 11 + .../Serialization/GenericsSnippets.cs | 79 +++++ .../Serialization/GenericsSnippets.cs.meta | 11 + .../Snippets/Serialization/MyDataStruct.cs | 10 + .../Serialization/MyDataStruct.cs.meta | 11 + .../Serialization/PropertiesExample.cs | 32 ++ .../Serialization/PropertiesExample.cs.meta | 11 + .../Serialization/StringStoreSnippets.cs | 127 ++++++++ .../Serialization/StringStoreSnippets.cs.meta | 11 + .../Snippets/Serialization/SwitchEnum.cs | 11 + .../Snippets/Serialization/SwitchEnum.cs.meta | 11 + .../Serialization/UnsupportedTypeExample.cs | 67 ++++ .../UnsupportedTypeExample.cs.meta | 11 + .../{ => Serialization}/UsingSyncPrefab.cs | 2 +- .../UsingSyncPrefab.cs.meta | 2 +- Assets/Mirage/Samples~/Snippets/Spawning.meta | 8 + .../{ => Spawning}/DynamicSpawning.cs | 0 .../{ => Spawning}/DynamicSpawning.cs.meta | 2 +- Assets/Mirage/Samples~/Snippets/Sync.meta | 8 + .../Samples~/Snippets/Sync/CodeGeneration.cs | 126 ++++++++ .../Snippets/Sync/CodeGeneration.cs.meta | 11 + .../Snippets/Sync/CustomSerialization.cs | 16 + .../Snippets/Sync/CustomSerialization.cs.meta | 11 + .../Snippets/Sync/SyncDictionaryExamples.cs | 75 +++++ .../Sync/SyncDictionaryExamples.cs.meta | 11 + .../Snippets/Sync/SyncHashSetExamples.cs | 67 ++++ .../Snippets/Sync/SyncHashSetExamples.cs.meta | 11 + .../Snippets/Sync/SyncListExamples.cs | 92 ++++++ .../Snippets/Sync/SyncListExamples.cs.meta | 11 + .../Snippets/Sync/SyncSortedSetExamples.cs | 63 ++++ .../Sync/SyncSortedSetExamples.cs.meta | 11 + .../Samples~/Snippets/Sync/SyncVarExamples.cs | 188 +++++++++++ .../Snippets/Sync/SyncVarExamples.cs.meta | 11 + .../Snippets/Sync/SyncVarHookExamples.cs | 32 ++ .../Snippets/Sync/SyncVarHookExamples.cs.meta | 11 + doc/docs/analyzers/MIRAGE1001.md | 47 +-- doc/docs/analyzers/MIRAGE1002.md | 23 +- doc/docs/analyzers/MIRAGE1003.md | 44 +-- doc/docs/analyzers/MIRAGE1004.md | 33 +- doc/docs/analyzers/MIRAGE1101.md | 22 +- doc/docs/analyzers/MIRAGE1201.md | 41 +-- doc/docs/analyzers/MIRAGE1202.md | 31 +- doc/docs/analyzers/MIRAGE1203.md | 28 +- doc/docs/analyzers/MIRAGE1204.md | 42 +-- doc/docs/analyzers/MIRAGE1205.md | 28 +- doc/docs/analyzers/MIRAGE1206.md | 27 +- doc/docs/analyzers/MIRAGE1301.md | 49 +-- doc/docs/analyzers/MIRAGE1302.md | 23 +- doc/docs/analyzers/MIRAGE1303.md | 43 +-- doc/docs/analyzers/MIRAGE1401.md | 35 +-- doc/docs/analyzers/MIRAGE1402.md | 49 +-- doc/docs/analyzers/MIRAGE1501.md | 23 +- doc/docs/analyzers/MIRAGE1502.md | 24 +- doc/docs/analyzers/MIRAGE1503.md | 31 +- doc/docs/components/network-discovery.md | 59 +--- doc/docs/components/network-log-settings.md | 48 +-- doc/docs/components/network-scene-checker.md | 28 +- doc/docs/components/network-scene-manager.md | 11 +- doc/docs/components/ready-check.md | 10 +- doc/docs/general/getting-started.md | 48 +-- doc/docs/general/troubleshooting.md | 22 +- doc/docs/guides/attributes.md | 19 +- .../authentication/basic-authenticator.md | 4 +- .../authentication/custom-authenticator.md | 10 +- doc/docs/guides/authority.md | 22 +- doc/docs/guides/best-practices.md | 8 +- .../bit-packing/bit-count-from-range.md | 67 +--- doc/docs/guides/bit-packing/bit-count.md | 61 +--- doc/docs/guides/bit-packing/float-pack.md | 64 +--- .../guides/bit-packing/quaternion-pack.md | 56 +--- doc/docs/guides/bit-packing/var-int-blocks.md | 67 +--- doc/docs/guides/bit-packing/vector-pack.md | 87 +----- doc/docs/guides/bit-packing/zig-zag-encode.md | 53 +--- .../guides/callbacks/network-behaviour.md | 24 +- doc/docs/guides/clock-sync.md | 20 +- .../mirage-quick-start-guide.md | 264 +--------------- doc/docs/guides/error-handling.md | 148 +-------- doc/docs/guides/faq.md | 14 +- doc/docs/guides/game-objects/lifecycle.md | 59 +--- .../guides/game-objects/pickup-drop-child.md | 199 +----------- .../game-objects/player-proxy-pattern.md | 60 +--- doc/docs/guides/game-objects/scene-objects.md | 35 +-- .../game-objects/spawn-object-custom.md | 68 +--- doc/docs/guides/game-objects/spawn-object.md | 146 +-------- .../game-objects/spawn-player-custom.md | 145 +-------- doc/docs/guides/remote-actions/client-rpc.md | 80 +---- .../guides/remote-actions/network-messages.md | 2 +- .../guides/remote-actions/rate-limiting.md | 31 +- .../guides/remote-actions/rpc-examples.md | 59 +--- doc/docs/guides/remote-actions/server-rpc.md | 58 +--- .../scene-loading/network-scene-loader.md | 38 +-- .../scene-loading/network-scene-manager.md | 96 +----- doc/docs/guides/serialization/advanced.md | 113 +------ doc/docs/guides/serialization/data-types.md | 160 +--------- doc/docs/guides/serialization/generics.md | 69 +---- doc/docs/guides/serialization/string-store.md | 96 +----- doc/docs/guides/serialization/sync-prefab.md | 2 +- doc/docs/guides/sync/code-generation.md | 112 +------ doc/docs/guides/sync/custom-serialization.md | 8 +- .../sync/sync-objects/sync-dictionary.md | 70 +---- .../guides/sync/sync-objects/sync-hash-set.md | 58 +--- .../guides/sync/sync-objects/sync-list.md | 76 +---- .../sync/sync-objects/sync-sorted-set.md | 54 +--- doc/docs/guides/sync/sync-var-hooks.md | 18 +- doc/docs/guides/sync/sync-var.md | 191 +----------- 268 files changed, 6173 insertions(+), 3743 deletions(-) create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1001.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1001.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1002.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1002.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1003.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1003.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1004.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1004.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1101.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1101.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1201.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1201.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1202.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1202.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1203.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1203.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1204.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1204.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1205.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1205.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1206.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1206.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1301.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1301.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1302.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1302.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1303.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1303.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1401.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1401.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1402.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1402.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1501.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1501.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1502.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1502.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1503.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1503.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Authentication.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Authentication/BasicAuthenticatorSnippets.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Authentication/BasicAuthenticatorSnippets.cs.meta rename Assets/Mirage/Samples~/Snippets/{ => Authentication}/CustomAuthenticator.cs (100%) rename Assets/Mirage/Samples~/Snippets/{ => Authentication}/CustomAuthenticator.cs.meta (83%) create mode 100644 Assets/Mirage/Samples~/Snippets/BitPacking.meta create mode 100644 Assets/Mirage/Samples~/Snippets/BitPacking/BitCountFromRangeSnippets.cs create mode 100644 Assets/Mirage/Samples~/Snippets/BitPacking/BitCountFromRangeSnippets.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/BitPacking/BitCountSnippets.cs create mode 100644 Assets/Mirage/Samples~/Snippets/BitPacking/BitCountSnippets.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/BitPacking/FloatPackSnippets.cs create mode 100644 Assets/Mirage/Samples~/Snippets/BitPacking/FloatPackSnippets.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/BitPacking/QuaternionPackSnippets.cs create mode 100644 Assets/Mirage/Samples~/Snippets/BitPacking/QuaternionPackSnippets.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/BitPacking/VarIntBlocksSnippets.cs create mode 100644 Assets/Mirage/Samples~/Snippets/BitPacking/VarIntBlocksSnippets.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/BitPacking/VectorPackSnippets.cs create mode 100644 Assets/Mirage/Samples~/Snippets/BitPacking/VectorPackSnippets.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/BitPacking/ZigZagEncodeSnippets.cs create mode 100644 Assets/Mirage/Samples~/Snippets/BitPacking/ZigZagEncodeSnippets.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Callbacks.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Callbacks/NetworkBehaviourCallbacks.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Callbacks/NetworkBehaviourCallbacks.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/CommunityGuides.meta create mode 100644 Assets/Mirage/Samples~/Snippets/CommunityGuides/QuickStartGuide.cs create mode 100644 Assets/Mirage/Samples~/Snippets/CommunityGuides/QuickStartGuide.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Components.meta rename Assets/Mirage/Samples~/Snippets/{ => Components}/LobbyReadyCheck.cs (97%) rename Assets/Mirage/Samples~/Snippets/{ => Components}/LobbyReadyCheck.cs.meta (83%) create mode 100644 Assets/Mirage/Samples~/Snippets/Components/NetworkDiscoverySnippet.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Components/NetworkDiscoverySnippet.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Components/NetworkLogSettingsSnippet.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Components/NetworkLogSettingsSnippet.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Components/NetworkSceneCheckerSnippet.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Components/NetworkSceneCheckerSnippet.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Components/NetworkSceneManagerSnippet.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Components/NetworkSceneManagerSnippet.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects.meta create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/CustomPlayerSpawning.cs create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/CustomPlayerSpawning.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/CustomSpawnExample.cs create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/CustomSpawnExample.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/LifecycleComponent.cs create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/LifecycleComponent.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/MyNetworkManager.cs create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/MyNetworkManager.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/NetworkWorldEvents.cs create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/NetworkWorldEvents.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/PlayerCharacter.cs create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/PlayerCharacter.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/PlayerContext.cs create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/PlayerContext.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/PlayerEquip.cs create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/PlayerEquip.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/PlayerEquipInitial.cs create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/PlayerEquipInitial.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/PlayerProxyManager.cs create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/PlayerProxyManager.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/SceneObject.cs create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/SceneObject.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/SceneObjectFilterExample.cs create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/SceneObjectFilterExample.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/SpawningExample.cs create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/SpawningExample.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/Tree.cs create mode 100644 Assets/Mirage/Samples~/Snippets/GameObjects/Tree.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/General.meta create mode 100644 Assets/Mirage/Samples~/Snippets/General/AttributesSnippets.cs create mode 100644 Assets/Mirage/Samples~/Snippets/General/AttributesSnippets.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/General/AuthoritySnippets.cs create mode 100644 Assets/Mirage/Samples~/Snippets/General/AuthoritySnippets.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/General/BestPracticesSnippets.cs create mode 100644 Assets/Mirage/Samples~/Snippets/General/BestPracticesSnippets.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/General/ClockSyncSnippets.cs create mode 100644 Assets/Mirage/Samples~/Snippets/General/ClockSyncSnippets.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/General/ErrorHandlingSnippets.cs create mode 100644 Assets/Mirage/Samples~/Snippets/General/ErrorHandlingSnippets.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/General/GettingStartedSnippets.cs create mode 100644 Assets/Mirage/Samples~/Snippets/General/GettingStartedSnippets.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/General/TroubleshootingSnippets.cs create mode 100644 Assets/Mirage/Samples~/Snippets/General/TroubleshootingSnippets.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Guides.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Guides/FaqSnippets.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Guides/FaqSnippets.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Messaging.meta rename Assets/Mirage/Samples~/Snippets/{ => Messaging}/SendNetworkMessage.cs (96%) rename Assets/Mirage/Samples~/Snippets/{ => Messaging}/SendNetworkMessage.cs.meta (83%) create mode 100644 Assets/Mirage/Samples~/Snippets/RemoteActions.meta create mode 100644 Assets/Mirage/Samples~/Snippets/RemoteActions/ClientRpcExamples.cs create mode 100644 Assets/Mirage/Samples~/Snippets/RemoteActions/ClientRpcExamples.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/RemoteActions/RateLimitingExamples.cs create mode 100644 Assets/Mirage/Samples~/Snippets/RemoteActions/RateLimitingExamples.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/RemoteActions/RpcExampleChangeName.cs create mode 100644 Assets/Mirage/Samples~/Snippets/RemoteActions/RpcExampleChangeName.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/RemoteActions/RpcExampleChangeNameGenerated.cs create mode 100644 Assets/Mirage/Samples~/Snippets/RemoteActions/RpcExampleChangeNameGenerated.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/RemoteActions/ServerRpcDoor.cs create mode 100644 Assets/Mirage/Samples~/Snippets/RemoteActions/ServerRpcDoor.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/RemoteActions/ServerRpcDropCube.cs create mode 100644 Assets/Mirage/Samples~/Snippets/RemoteActions/ServerRpcDropCube.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Rpc.meta rename Assets/Mirage/Samples~/Snippets/{ => Rpc}/RpcReply.cs (97%) rename Assets/Mirage/Samples~/Snippets/{ => Rpc}/RpcReply.cs.meta (83%) create mode 100644 Assets/Mirage/Samples~/Snippets/SceneLoading.meta create mode 100644 Assets/Mirage/Samples~/Snippets/SceneLoading/CustomSceneManager.cs create mode 100644 Assets/Mirage/Samples~/Snippets/SceneLoading/CustomSceneManager.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/SceneLoading/FixedMatchSize.cs create mode 100644 Assets/Mirage/Samples~/Snippets/SceneLoading/FixedMatchSize.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneAdditively.cs create mode 100644 Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneAdditively.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneAdditivelyPlayer.cs create mode 100644 Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneAdditivelyPlayer.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneNormal.cs create mode 100644 Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneNormal.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneNormalParams.cs create mode 100644 Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneNormalParams.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/SceneLoading/LoaderUsage.cs create mode 100644 Assets/Mirage/Samples~/Snippets/SceneLoading/LoaderUsage.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/SceneLoading/UnLoadSceneAdditively.cs create mode 100644 Assets/Mirage/Samples~/Snippets/SceneLoading/UnLoadSceneAdditively.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Serialization.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Serialization/CustomReadWrite.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Serialization/CustomReadWrite.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Serialization/DataTypesSnippets.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Serialization/DataTypesSnippets.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Serialization/GenericsSnippets.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Serialization/GenericsSnippets.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Serialization/MyDataStruct.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Serialization/MyDataStruct.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Serialization/PropertiesExample.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Serialization/PropertiesExample.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Serialization/StringStoreSnippets.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Serialization/StringStoreSnippets.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Serialization/SwitchEnum.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Serialization/SwitchEnum.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Serialization/UnsupportedTypeExample.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Serialization/UnsupportedTypeExample.cs.meta rename Assets/Mirage/Samples~/Snippets/{ => Serialization}/UsingSyncPrefab.cs (95%) rename Assets/Mirage/Samples~/Snippets/{ => Serialization}/UsingSyncPrefab.cs.meta (83%) create mode 100644 Assets/Mirage/Samples~/Snippets/Spawning.meta rename Assets/Mirage/Samples~/Snippets/{ => Spawning}/DynamicSpawning.cs (100%) rename Assets/Mirage/Samples~/Snippets/{ => Spawning}/DynamicSpawning.cs.meta (83%) create mode 100644 Assets/Mirage/Samples~/Snippets/Sync.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Sync/CodeGeneration.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Sync/CodeGeneration.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Sync/CustomSerialization.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Sync/CustomSerialization.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Sync/SyncDictionaryExamples.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Sync/SyncDictionaryExamples.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Sync/SyncHashSetExamples.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Sync/SyncHashSetExamples.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Sync/SyncListExamples.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Sync/SyncListExamples.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Sync/SyncSortedSetExamples.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Sync/SyncSortedSetExamples.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Sync/SyncVarExamples.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Sync/SyncVarExamples.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Sync/SyncVarHookExamples.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Sync/SyncVarHookExamples.cs.meta diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers.meta b/Assets/Mirage/Samples~/Snippets/Analyzers.meta new file mode 100644 index 00000000000..f5a925903bd --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 442ad51ea2b31f74f8fd558e7ca316d7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1001.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1001.cs new file mode 100644 index 00000000000..fd463dc03b8 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1001.cs @@ -0,0 +1,69 @@ +using Mirage; + +namespace Mirage.Snippets.Analyzers +{ + namespace M1001.Triggering + { + // CodeEmbed-Start: mirage1001-triggering + public class PlayerData + { + public int health; + public string name; + } + + public class Player : NetworkBehaviour + { + // Warns: SyncVar 'data' is a class type 'PlayerData'. + [SyncVar] + public PlayerData data { get; set; } + } + // CodeEmbed-End: mirage1001-triggering + } + + namespace M1001.StructOption + { + // CodeEmbed-Start: mirage1001-struct-option + public struct PlayerData + { + public int health; + public string name; + } + + public class Player : NetworkBehaviour + { + [SyncVar] + public PlayerData data { get; set; } + } + // CodeEmbed-End: mirage1001-struct-option + } + + namespace M1001.ClassOption + { + // CodeEmbed-Start: mirage1001-class-option + [WeaverSafeClass] + public class PlayerData + { + public int health; + public string name; + } + // CodeEmbed-End: mirage1001-class-option + } + + namespace M1001.SuppressOption + { + // CodeEmbed-Start: mirage1001-suppress-option + public class PlayerData + { + public int health; + public string name; + } + + public class Player : NetworkBehaviour + { + [SyncVar] + [WeaverSafeClass] + public PlayerData data { get; set; } + } + // CodeEmbed-End: mirage1001-suppress-option + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1001.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1001.cs.meta new file mode 100644 index 00000000000..ad6c569dcf1 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1001.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 768abe28a17bdc94b9312dbf674e58f1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1002.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1002.cs new file mode 100644 index 00000000000..312c2be8118 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1002.cs @@ -0,0 +1,33 @@ +using Mirage; + +namespace Mirage.Snippets.Analyzers +{ + namespace M1002.Triggering + { + // CodeEmbed-Start: mirage1002-triggering + public class Player : NetworkBehaviour + { + private int _health; + + // Errors: SyncVar property 'health' must be a non-static auto-property... + [SyncVar] + public int health + { + get => _health; + set => _health = value; + } + } + // CodeEmbed-End: mirage1002-triggering + } + + namespace M1002.Resolved + { + // CodeEmbed-Start: mirage1002-resolved + public class Player : NetworkBehaviour + { + [SyncVar] + public int health { get; set; } + } + // CodeEmbed-End: mirage1002-resolved + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1002.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1002.cs.meta new file mode 100644 index 00000000000..a7e289af84f --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1002.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 22ad32c2f32a4c845b9c2d1225e48eaa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1003.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1003.cs new file mode 100644 index 00000000000..b12ef489cc6 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1003.cs @@ -0,0 +1,49 @@ +using Mirage; +using Mirage.Collections; + +namespace Mirage.Snippets.Analyzers +{ + namespace M1003.Triggering + { + // CodeEmbed-Start: mirage1003-triggering + public struct PlayerData + { + public int health; + } + + public class Player : NetworkBehaviour + { + public readonly SyncList playerList = new SyncList(); + + public void DamagePlayer(int index, int damage) + { + // Warning: Direct mutation of elements inside playerList is not supported because changes cannot be tracked. + playerList[index].health -= damage; + } + } + // CodeEmbed-End: mirage1003-triggering + } + + namespace M1003.Resolved + { + // CodeEmbed-Start: mirage1003-resolved + public struct PlayerData + { + public int health; + } + + public class Player : NetworkBehaviour + { + public readonly SyncList playerList = new SyncList(); + + public void DamagePlayer(int index, int damage) + { + // Correct: Modifying the element and setting it back, triggering the index setter + var data = playerList[index]; + data.health -= damage; + playerList[index] = data; + } + } + // CodeEmbed-End: mirage1003-resolved + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1003.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1003.cs.meta new file mode 100644 index 00000000000..f880536dc1f --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1003.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4bf64f84502dc5e478522287549ac331 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1004.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1004.cs new file mode 100644 index 00000000000..5235789f7eb --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1004.cs @@ -0,0 +1,38 @@ +using Mirage; +using Mirage.Collections; + +namespace Mirage.Snippets.Analyzers +{ + namespace M1004.Triggering + { + // CodeEmbed-Start: mirage1004-triggering + public class Player : NetworkBehaviour + { + // Error: ISyncObject field 'playerList' must be marked readonly and cannot be reassigned + public SyncList playerList = new SyncList(); + + public void ResetList() + { + playerList = new SyncList(); + } + } + // CodeEmbed-End: mirage1004-triggering + } + + namespace M1004.Resolved + { + // CodeEmbed-Start: mirage1004-resolved + public class Player : NetworkBehaviour + { + // Correct: Marked as readonly + public readonly SyncList playerList = new SyncList(); + + public void ResetList() + { + // Correct: Clear the list instead of reassigning it + playerList.Clear(); + } + } + // CodeEmbed-End: mirage1004-resolved + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1004.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1004.cs.meta new file mode 100644 index 00000000000..1efb3661231 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1004.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: efc4b8394906a9744bfbd28519da22e2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1101.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1101.cs new file mode 100644 index 00000000000..c963d63f5bd --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1101.cs @@ -0,0 +1,28 @@ +using UnityEngine; +using Mirage; + +namespace Mirage.Snippets.Analyzers +{ + namespace M1101.Triggering + { + // CodeEmbed-Start: mirage1101-triggering + public class GameManager : MonoBehaviour + { + // Errors: Attribute 'SyncVarAttribute' cannot be used on 'score'... + [SyncVar] + public int score { get; set; } + } + // CodeEmbed-End: mirage1101-triggering + } + + namespace M1101.Resolved + { + // CodeEmbed-Start: mirage1101-resolved + public class GameManager : NetworkBehaviour + { + [SyncVar] + public int score { get; set; } + } + // CodeEmbed-End: mirage1101-resolved + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1101.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1101.cs.meta new file mode 100644 index 00000000000..9cba65a0aaa --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1101.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eddfa82ae9166cd46bb67d20d0dc718d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1201.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1201.cs new file mode 100644 index 00000000000..b9c07e0ea93 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1201.cs @@ -0,0 +1,48 @@ +using Mirage; +using Cysharp.Threading.Tasks; + +namespace Mirage.Snippets.Analyzers +{ + public struct PlayerStats {} + + namespace M1201.Triggering + { + // CodeEmbed-Start: mirage1201-triggering + public class Player : NetworkBehaviour + { + // Errors: RPC method 'CmdTakeDamage' is invalid: cannot have generic parameters. + [ServerRpc] + public void CmdTakeDamage(T damage) + { + } + + // Errors: RPC method 'CmdGetStats' is invalid: cannot return 'PlayerStats'... + [ServerRpc] + public PlayerStats CmdGetStats() + { + return new PlayerStats(); + } + } + // CodeEmbed-End: mirage1201-triggering + } + + namespace M1201.Resolved + { + // CodeEmbed-Start: mirage1201-resolved + public class Player : NetworkBehaviour + { + [ServerRpc] + public void CmdTakeDamage(int damage) + { + } + + [ServerRpc] + public async UniTask CmdGetStats() + { + await UniTask.Yield(); + return new PlayerStats(); + } + } + // CodeEmbed-End: mirage1201-resolved + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1201.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1201.cs.meta new file mode 100644 index 00000000000..89fecf9b246 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1201.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2e9bc50965d73924196b0b950dd24267 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1202.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1202.cs new file mode 100644 index 00000000000..0a7d4d645ff --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1202.cs @@ -0,0 +1,37 @@ +using Mirage; + +namespace Mirage.Snippets.Analyzers +{ + namespace M1202.Triggering + { + // CodeEmbed-Start: mirage1202-triggering + public class Player : NetworkBehaviour + { + // Error: ServerRpc method 'CmdTakeDamage' cannot have ref/out parameters + [ServerRpc] + public void CmdTakeDamage(ref int health) + { + health -= 10; + } + } + // CodeEmbed-End: mirage1202-triggering + } + + namespace M1202.Resolved + { + // CodeEmbed-Start: mirage1202-resolved + public class Player : NetworkBehaviour + { + [SyncVar] + public int Health { get; set; } + + // Correct: Pass by value and synchronize via SyncVar + [ServerRpc] + public void CmdTakeDamage(int damage) + { + Health -= damage; + } + } + // CodeEmbed-End: mirage1202-resolved + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1202.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1202.cs.meta new file mode 100644 index 00000000000..077b896035c --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1202.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bc06b22e2a9707542a47199cbd77c102 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1203.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1203.cs new file mode 100644 index 00000000000..ecc207d388c --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1203.cs @@ -0,0 +1,34 @@ +using Mirage; + +namespace Mirage.Snippets.Analyzers +{ + namespace M1203.Triggering + { + // CodeEmbed-Start: mirage1203-triggering + public class Player : NetworkBehaviour + { + // Error: ServerRpc method 'CmdSpawnGlobal' must not be static + [ServerRpc] + public static void CmdSpawnGlobal() + { + // Static context has no NetworkIdentity + } + } + // CodeEmbed-End: mirage1203-triggering + } + + namespace M1203.Resolved + { + // CodeEmbed-Start: mirage1203-resolved + public class Player : NetworkBehaviour + { + // Correct: Instance method has access to the NetworkBehaviour state + [ServerRpc] + public void CmdSpawn() + { + // Normal instance context + } + } + // CodeEmbed-End: mirage1203-resolved + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1203.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1203.cs.meta new file mode 100644 index 00000000000..c45b8bf5dc7 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1203.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2c3ff2f87110934488cec1a60649f4c0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1204.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1204.cs new file mode 100644 index 00000000000..ae488d6715a --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1204.cs @@ -0,0 +1,47 @@ +using Mirage; +using Cysharp.Threading.Tasks; + +namespace Mirage.Snippets.Analyzers +{ + namespace M1204.Triggering + { + // CodeEmbed-Start: mirage1204-triggering + public class Player : NetworkBehaviour + { + // Error: [ClientRpc] must return void when target is Observers. + [ClientRpc(target = RpcTarget.Observers)] + public UniTask RpcGetHealth() + { + return UniTask.FromResult(100); + } + + // Error: ClientRpc method with target = Player requires first parameter to be INetworkPlayer + [ClientRpc(target = RpcTarget.Player)] + public void RpcGiveItem(int itemId) + { + } + } + // CodeEmbed-End: mirage1204-triggering + } + + namespace M1204.Resolved + { + // CodeEmbed-Start: mirage1204-resolved + public class Player : NetworkBehaviour + { + // Correct: Targeted RPC returning value to the Owner + [ClientRpc(target = RpcTarget.Owner)] + public UniTask RpcGetHealth() + { + return UniTask.FromResult(100); + } + + // Correct: First parameter is the target player connection + [ClientRpc(target = RpcTarget.Player)] + public void RpcGiveItem(INetworkPlayer targetPlayer, int itemId) + { + } + } + // CodeEmbed-End: mirage1204-resolved + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1204.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1204.cs.meta new file mode 100644 index 00000000000..b2883b0e66f --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1204.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 35b2312346a12964da23fedf5c3127ab +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1205.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1205.cs new file mode 100644 index 00000000000..326eabb1692 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1205.cs @@ -0,0 +1,34 @@ +using Mirage; + +namespace Mirage.Snippets.Analyzers +{ + namespace M1205.Triggering + { + // CodeEmbed-Start: mirage1205-triggering + public class Player : NetworkBehaviour + { + // Error: RateLimit interval must be greater than zero, and MaxTokens must be >= Refill + [ServerRpc] + [RateLimit(Interval = -0.5f, Refill = 10, MaxTokens = 5)] + public void CmdSpammyAction() + { + } + } + // CodeEmbed-End: mirage1205-triggering + } + + namespace M1205.Resolved + { + // CodeEmbed-Start: mirage1205-resolved + public class Player : NetworkBehaviour + { + // Correct: Positive interval and MaxTokens >= Refill + [ServerRpc] + [RateLimit(Interval = 1.0f, Refill = 10, MaxTokens = 20)] + public void CmdSpammyAction() + { + } + } + // CodeEmbed-End: mirage1205-resolved + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1205.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1205.cs.meta new file mode 100644 index 00000000000..98ce7f89d10 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1205.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dd7d09e6253794e429a3ef160411edb4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1206.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1206.cs new file mode 100644 index 00000000000..1c0f62c871c --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1206.cs @@ -0,0 +1,33 @@ +using Mirage; + +namespace Mirage.Snippets.Analyzers +{ + namespace M1206.Triggering + { + // CodeEmbed-Start: mirage1206-triggering + public class Player : NetworkBehaviour + { + // Warning: ServerRpc 'CmdFireWeapon' should have a [RateLimit] attribute to prevent spam + [ServerRpc] + public void CmdFireWeapon() + { + } + } + // CodeEmbed-End: mirage1206-triggering + } + + namespace M1206.Resolved + { + // CodeEmbed-Start: mirage1206-resolved + public class Player : NetworkBehaviour + { + // Correct: ServerRpc decorated with [RateLimit] to throttle client requests + [ServerRpc] + [RateLimit(Interval = 0.2f, Refill = 5, MaxTokens = 10)] + public void CmdFireWeapon() + { + } + } + // CodeEmbed-End: mirage1206-resolved + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1206.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1206.cs.meta new file mode 100644 index 00000000000..77eb1c266d7 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1206.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0c654e25127132b4786b7499366651ef +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1301.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1301.cs new file mode 100644 index 00000000000..41f782af186 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1301.cs @@ -0,0 +1,69 @@ +using Mirage; + +namespace Mirage.Snippets.Analyzers +{ + namespace M1301.Triggering + { + // CodeEmbed-Start: mirage1301-triggering + public class TargetInfo + { + public int x; + public int y; + } + + [NetworkMessage] + public struct FireMessage + { + // Warns: NetworkMessage field 'info' is a class type 'TargetInfo'. + public TargetInfo info; + } + // CodeEmbed-End: mirage1301-triggering + } + + namespace M1301.StructOption + { + // CodeEmbed-Start: mirage1301-struct-option + public struct TargetInfo + { + public int x; + public int y; + } + + [NetworkMessage] + public struct FireMessage + { + public TargetInfo info; + } + // CodeEmbed-End: mirage1301-struct-option + } + + namespace M1301.ClassOption + { + // CodeEmbed-Start: mirage1301-class-option + [WeaverSafeClass] + public class TargetInfo + { + public int x; + public int y; + } + // CodeEmbed-End: mirage1301-class-option + } + + namespace M1301.SuppressOption + { + // CodeEmbed-Start: mirage1301-suppress-option + [NetworkMessage] + public struct FireMessage + { + [WeaverSafeClass] + public TargetInfo info; + } + // CodeEmbed-End: mirage1301-suppress-option + + public class TargetInfo + { + public int x; + public int y; + } + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1301.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1301.cs.meta new file mode 100644 index 00000000000..706875fd625 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1301.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: adef096b2cf08ab48968cff17de54308 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1302.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1302.cs new file mode 100644 index 00000000000..15b12adaf63 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1302.cs @@ -0,0 +1,29 @@ +using Mirage; +using System.Threading; + +namespace Mirage.Snippets.Analyzers +{ + namespace M1302.Triggering + { + // CodeEmbed-Start: mirage1302-triggering + [NetworkMessage] + public struct StartSessionMessage + { + // Error: Field type 'Thread' is not serializable by Mirage. + public Thread executionThread; + } + // CodeEmbed-End: mirage1302-triggering + } + + namespace M1302.Resolved + { + // CodeEmbed-Start: mirage1302-resolved + [NetworkMessage] + public struct StartSessionMessage + { + // Correct: Pass a serializable identifier instead of the raw thread object + public string threadName; + } + // CodeEmbed-End: mirage1302-resolved + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1302.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1302.cs.meta new file mode 100644 index 00000000000..651c1181b94 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1302.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f17fe3396d839284c995fd290652bab6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1303.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1303.cs new file mode 100644 index 00000000000..0291e9cc0aa --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1303.cs @@ -0,0 +1,48 @@ +using Mirage; +using Mirage.Serialization; + +namespace Mirage.Snippets.Analyzers +{ + namespace M1303.Triggering + { + // CodeEmbed-Start: mirage1303-triggering + public struct CustomType + { + public int value; + } + + public static class CustomSerialization + { + // Error: Custom writer defined but matching custom reader is missing + public static void WriteCustomType(this NetworkWriter writer, CustomType value) + { + writer.WritePackedInt32(value.value); + } + } + // CodeEmbed-End: mirage1303-triggering + } + + namespace M1303.Resolved + { + // CodeEmbed-Start: mirage1303-resolved + public struct CustomType + { + public int value; + } + + public static class CustomSerialization + { + // Correct: Both writer and reader are defined with matching signatures + public static void WriteCustomType(this NetworkWriter writer, CustomType value) + { + writer.WritePackedInt32(value.value); + } + + public static CustomType ReadCustomType(this NetworkReader reader) + { + return new CustomType { value = reader.ReadPackedInt32() }; + } + } + // CodeEmbed-End: mirage1303-resolved + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1303.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1303.cs.meta new file mode 100644 index 00000000000..19935e539c4 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1303.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 34fc2b23a66d7cb4cb4e075c25f98758 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1401.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1401.cs new file mode 100644 index 00000000000..5af4b4b6d49 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1401.cs @@ -0,0 +1,42 @@ +namespace Mirage.Snippets.Analyzers +{ + namespace M1401.Triggering + { + // CodeEmbed-Start: mirage1401-triggering + public class PlayerHealth : NetworkBehaviour + { + [SyncVar] + public int Health { get; set; } + + private void Start() + { + // Warning: Accessing Network State (IsServer) in Awake/Start + if (IsServer) + Health = 100; + } + } + // CodeEmbed-End: mirage1401-triggering + } + + namespace M1401.Resolved + { + // CodeEmbed-Start: mirage1401-resolved + public class PlayerHealth : NetworkBehaviour + { + [SyncVar] + public int Health { get; set; } + + private void Awake() + { + Identity.OnStartServer.AddListener(OnStartServer); + } + + // Correct: Run server initialization when the network server has started + public void OnStartServer() + { + Health = 100; + } + } + // CodeEmbed-End: mirage1401-resolved + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1401.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1401.cs.meta new file mode 100644 index 00000000000..ded79f1a6fa --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1401.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 24dbdedf5fd0d494c8d7538ea0ededbd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1402.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1402.cs new file mode 100644 index 00000000000..5e0d6060d11 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1402.cs @@ -0,0 +1,54 @@ +using Mirage; +using Mirage.Serialization; + +namespace Mirage.Snippets.Analyzers +{ + namespace M1402.Triggering + { + // CodeEmbed-Start: mirage1402-triggering + public class BasePlayer : NetworkBehaviour + { + [SyncVar] + public string PlayerName { get; set; } + } + + public class HeroPlayer : BasePlayer + { + [SyncVar] + public int HeroId { get; set; } + + // Warning: Overriding OnSerialize without calling base.OnSerialize + public override bool OnSerialize(NetworkWriter writer, bool initialState) + { + writer.WritePackedInt32(HeroId); + return true; + } + } + // CodeEmbed-End: mirage1402-triggering + } + + namespace M1402.Resolved + { + // CodeEmbed-Start: mirage1402-resolved + public class BasePlayer : NetworkBehaviour + { + [SyncVar] + public string PlayerName { get; set; } + } + + public class HeroPlayer : BasePlayer + { + [SyncVar] + public int HeroId { get; set; } + + // Correct: Calls base.OnSerialize and combines dirty states + public override bool OnSerialize(NetworkWriter writer, bool initialState) + { + bool baseDirty = base.OnSerialize(writer, initialState); + writer.WritePackedInt32(HeroId); + return baseDirty || true; + } + } + // CodeEmbed-End: mirage1402-resolved + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1402.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1402.cs.meta new file mode 100644 index 00000000000..3c5e937ac86 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1402.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3b14a4c2e910a33419fc507808a8f745 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1501.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1501.cs new file mode 100644 index 00000000000..cb678254af9 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1501.cs @@ -0,0 +1,29 @@ +using Mirage; + +namespace Mirage.Snippets.Analyzers +{ + namespace M1501.Triggering + { + // CodeEmbed-Start: mirage1501-triggering + [NetworkMessage] + public struct HugeMessage + { + // Warning: Array size and primitives exceed the safe MTU threshold + public byte[] largeBuffer; // e.g. filled with 2048 bytes of data + } + // CodeEmbed-End: mirage1501-triggering + } + + namespace M1501.Resolved + { + // CodeEmbed-Start: mirage1501-resolved + [NetworkMessage] + public struct ChunkMessage + { + public int chunkIndex; + // Correct: Small buffer sizes that fit comfortably within a single MTU packet + public byte[] smallBuffer; // e.g. limited to 512 bytes per chunk + } + // CodeEmbed-End: mirage1501-resolved + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1501.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1501.cs.meta new file mode 100644 index 00000000000..f801244f4ad --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1501.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7a3f010bea5e3a3459c43020b6cb8a15 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1502.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1502.cs new file mode 100644 index 00000000000..904807b275c --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1502.cs @@ -0,0 +1,30 @@ +using Mirage; +using Mirage.Serialization; + +namespace Mirage.Snippets.Analyzers +{ + namespace M1502.Triggering + { + // CodeEmbed-Start: mirage1502-triggering + [NetworkMessage] + public struct ChatMessage + { + // Warning: Unbounded string can be exploited to send megabytes of text + public string text; + } + // CodeEmbed-End: mirage1502-triggering + } + + namespace M1502.Resolved + { + // CodeEmbed-Start: mirage1502-resolved + [NetworkMessage] + public struct ChatMessage + { + // Correct: Restrict the maximum string length using BitCount or other validation attributes + [BitCount(8)] + public string text; + } + // CodeEmbed-End: mirage1502-resolved + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1502.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1502.cs.meta new file mode 100644 index 00000000000..8dc7f8f81bf --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1502.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 25016016df9f5a5429a124decf0c2bc9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1503.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1503.cs new file mode 100644 index 00000000000..10b44762e02 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1503.cs @@ -0,0 +1,37 @@ +using Mirage; +using Mirage.Serialization; + +namespace Mirage.Snippets.Analyzers +{ + namespace M1503.Triggering + { + // CodeEmbed-Start: mirage1503-triggering + public class Player : NetworkBehaviour + { + // Warning: 'Health' uses uncompressed int which has high bit-overhead. + [SyncVar] + public int Health { get; set; } + + // Warning: 'PlayerScale' uses uncompressed float which has high bit-overhead. + [SyncVar] + public float PlayerScale { get; set; } + } + // CodeEmbed-End: mirage1503-triggering + } + + namespace M1503.Resolved + { + // CodeEmbed-Start: mirage1503-resolved + public class Player : NetworkBehaviour + { + // Correct: Restrict Health to 7 bits (0-127 range) + [SyncVar, BitCount(7)] + public int Health { get; set; } + + // Correct: Compress float with a defined range and precision + [SyncVar, FloatPack(-10f, 10f, 0.01f)] + public float PlayerScale { get; set; } + } + // CodeEmbed-End: mirage1503-resolved + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1503.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1503.cs.meta new file mode 100644 index 00000000000..2e49e1f1653 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1503.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 10fad916023430041a6bc426fd773565 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Authentication.meta b/Assets/Mirage/Samples~/Snippets/Authentication.meta new file mode 100644 index 00000000000..3cd38eaceb0 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Authentication.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 31aab5b69e5ca05418a427ed49747f5d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Authentication/BasicAuthenticatorSnippets.cs b/Assets/Mirage/Samples~/Snippets/Authentication/BasicAuthenticatorSnippets.cs new file mode 100644 index 00000000000..8391d773ef6 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Authentication/BasicAuthenticatorSnippets.cs @@ -0,0 +1,11 @@ +using Mirage; + +namespace Mirage.Snippets.Authentication +{ + public interface IBasicAuthenticator + { + // CodeEmbed-Start: basic-authenticator-sendcode + void SendCode(NetworkClient client, string serverCode = null); + // CodeEmbed-End: basic-authenticator-sendcode + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Authentication/BasicAuthenticatorSnippets.cs.meta b/Assets/Mirage/Samples~/Snippets/Authentication/BasicAuthenticatorSnippets.cs.meta new file mode 100644 index 00000000000..911dd449945 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Authentication/BasicAuthenticatorSnippets.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d37e0b2fab6938b479a6b03044a5a58f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/CustomAuthenticator.cs b/Assets/Mirage/Samples~/Snippets/Authentication/CustomAuthenticator.cs similarity index 100% rename from Assets/Mirage/Samples~/Snippets/CustomAuthenticator.cs rename to Assets/Mirage/Samples~/Snippets/Authentication/CustomAuthenticator.cs diff --git a/Assets/Mirage/Samples~/Snippets/CustomAuthenticator.cs.meta b/Assets/Mirage/Samples~/Snippets/Authentication/CustomAuthenticator.cs.meta similarity index 83% rename from Assets/Mirage/Samples~/Snippets/CustomAuthenticator.cs.meta rename to Assets/Mirage/Samples~/Snippets/Authentication/CustomAuthenticator.cs.meta index e077acf26c3..acffdcc85e0 100644 --- a/Assets/Mirage/Samples~/Snippets/CustomAuthenticator.cs.meta +++ b/Assets/Mirage/Samples~/Snippets/Authentication/CustomAuthenticator.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: ba932883a0cae144384e9bab9d0b6e0a +guid: 582ef689b7b07234499d3390fc2da7bd MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/Mirage/Samples~/Snippets/BitPacking.meta b/Assets/Mirage/Samples~/Snippets/BitPacking.meta new file mode 100644 index 00000000000..9d13763013f --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/BitPacking.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 323a51acc68809b42a1c996e63766bfc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/BitPacking/BitCountFromRangeSnippets.cs b/Assets/Mirage/Samples~/Snippets/BitPacking/BitCountFromRangeSnippets.cs new file mode 100644 index 00000000000..d2a8fc53f07 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/BitPacking/BitCountFromRangeSnippets.cs @@ -0,0 +1,86 @@ +using Mirage; +using Mirage.Serialization; + +namespace Mirage.Snippets.BitPacking.BitCountFromRangeExample1 +{ + // CodeEmbed-Start: bit-count-from-range-example-1 + public class MyNetworkBehaviour : NetworkBehaviour + { + [SyncVar, BitCountFromRange(-100, 100)] + public int modifier { get; set; } + } + // CodeEmbed-End: bit-count-from-range-example-1 +} + +namespace Mirage.Snippets.BitPacking.BitCountFromRangeExample2 +{ + internal class BitCountAttribute : System.Attribute + { + public BitCountAttribute(int min, int max) {} + } + + // CodeEmbed-Start: bit-count-from-range-example-2 + public enum MyDirection + { + Backwards = -1, + None = 0, + Forwards = 1, + } + public class MyNetworkBehaviour : NetworkBehaviour + { + [SyncVar, BitCount(-1, 1)] + public MyDirection direction { get; set; } + } + // CodeEmbed-End: bit-count-from-range-example-2 +} + +namespace Mirage.Snippets.BitPacking.BitCountFromRangeGenerated +{ + public class GeneratedExample : NetworkBehaviour + { + // CodeEmbed-Start: bit-count-from-range-generated-source + [SyncVar, BitCountFromRange(-100, 100)] + public int myValue { get; set; } + // CodeEmbed-End: bit-count-from-range-generated-source + + // CodeEmbed-Start: bit-count-from-range-generated-code + public override bool SerializeSyncVars(NetworkWriter writer, bool initialState) + { + ulong syncVarDirtyBits = base.SyncVarDirtyBits; + bool result = base.SerializeSyncVars(writer, initialState); + + if (initialState) + { + writer.Write((ulong)(this.myValue - (-100)), 8); + return true; + } + + writer.Write(syncVarDirtyBits, 1); + if ((syncVarDirtyBits & 1UL) != 0UL) + { + writer.Write((ulong)(this.myValue - (-100)), 8); + result = true; + } + + return result; + } + + public override void DeserializeSyncVars(NetworkReader reader, bool initialState) + { + base.DeserializeSyncVars(reader, initialState); + + if (initialState) + { + this.myValue = (int)reader.Read(8) + (-100); + return; + } + + ulong dirtyMask = reader.Read(1); + if ((dirtyMask & 1UL) != 0UL) + { + this.myValue = (int)reader.Read(8) + (-100); + } + } + // CodeEmbed-End: bit-count-from-range-generated-code + } +} diff --git a/Assets/Mirage/Samples~/Snippets/BitPacking/BitCountFromRangeSnippets.cs.meta b/Assets/Mirage/Samples~/Snippets/BitPacking/BitCountFromRangeSnippets.cs.meta new file mode 100644 index 00000000000..e0648fa411e --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/BitPacking/BitCountFromRangeSnippets.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4a8620d999af4f645bd680a6f742ed38 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/BitPacking/BitCountSnippets.cs b/Assets/Mirage/Samples~/Snippets/BitPacking/BitCountSnippets.cs new file mode 100644 index 00000000000..c6c50558d04 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/BitPacking/BitCountSnippets.cs @@ -0,0 +1,75 @@ +using Mirage; +using Mirage.Serialization; + +namespace Mirage.Snippets.BitPacking.BitCountExample1 +{ + // CodeEmbed-Start: bit-count-example-1 + public class MyNetworkBehaviour : NetworkBehaviour + { + [SyncVar, BitCount(7)] + public int Health { get; set; } + } + // CodeEmbed-End: bit-count-example-1 +} + +namespace Mirage.Snippets.BitPacking.BitCountExample2 +{ + // CodeEmbed-Start: bit-count-example-2 + public class MyNetworkBehaviour : NetworkBehaviour + { + [SyncVar, BitCount(3)] + public int WeaponIndex { get; set; } + } + // CodeEmbed-End: bit-count-example-2 +} + +namespace Mirage.Snippets.BitPacking.BitCountGenerated +{ + public class GeneratedExample : NetworkBehaviour + { + // CodeEmbed-Start: bit-count-generated-source + [SyncVar, BitCount(7)] + public int myValue { get; set; } + // CodeEmbed-End: bit-count-generated-source + + // CodeEmbed-Start: bit-count-generated-code + public override bool SerializeSyncVars(NetworkWriter writer, bool initialState) + { + ulong syncVarDirtyBits = base.SyncVarDirtyBits; + bool result = base.SerializeSyncVars(writer, initialState); + + if (initialState) + { + writer.Write((ulong)this.myValue, 7); + return true; + } + + writer.Write(syncVarDirtyBits, 1); + if ((syncVarDirtyBits & 1UL) != 0UL) + { + writer.Write((ulong)this.myValue, 7); + result = true; + } + + return result; + } + + public override void DeserializeSyncVars(NetworkReader reader, bool initialState) + { + base.DeserializeSyncVars(reader, initialState); + + if (initialState) + { + this.myValue = (int)reader.Read(7); + return; + } + + ulong dirtyMask = reader.Read(1); + if ((dirtyMask & 1UL) != 0UL) + { + this.myValue = (int)reader.Read(7); + } + } + // CodeEmbed-End: bit-count-generated-code + } +} diff --git a/Assets/Mirage/Samples~/Snippets/BitPacking/BitCountSnippets.cs.meta b/Assets/Mirage/Samples~/Snippets/BitPacking/BitCountSnippets.cs.meta new file mode 100644 index 00000000000..f9fd10d1903 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/BitPacking/BitCountSnippets.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 25f26fac7ee132248880247f15f7c80d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/BitPacking/FloatPackSnippets.cs b/Assets/Mirage/Samples~/Snippets/BitPacking/FloatPackSnippets.cs new file mode 100644 index 00000000000..c283d3fa4a1 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/BitPacking/FloatPackSnippets.cs @@ -0,0 +1,77 @@ +using Mirage; +using Mirage.Serialization; + +namespace Mirage.Snippets.BitPacking.FloatPackExample1 +{ + // CodeEmbed-Start: float-pack-example-1 + public class MyNetworkBehaviour : NetworkBehaviour + { + [SyncVar, FloatPack(100f, 0.02f)] + public float Health { get; set; } + } + // CodeEmbed-End: float-pack-example-1 +} + +namespace Mirage.Snippets.BitPacking.FloatPackExample2 +{ + // CodeEmbed-Start: float-pack-example-2 + public class MyNetworkBehaviour : NetworkBehaviour + { + [SyncVar, FloatPack(1f, 8)] + public float Percent { get; set; } + } + // CodeEmbed-End: float-pack-example-2 +} + +namespace Mirage.Snippets.BitPacking.FloatPackGenerated +{ + public class GeneratedExample : NetworkBehaviour + { + // CodeEmbed-Start: float-pack-generated-source + [SyncVar, FloatPack(100f, 0.02f)] + public float myValue { get; set; } + // CodeEmbed-End: float-pack-generated-source + + // CodeEmbed-Start: float-pack-generated-code + private FloatPacker myValue__Packer = new FloatPacker(100f, 0.02f); + + public override bool SerializeSyncVars(NetworkWriter writer, bool initialState) + { + ulong syncVarDirtyBits = base.SyncVarDirtyBits; + bool result = base.SerializeSyncVars(writer, initialState); + + if (initialState) + { + myValue__Packer.Pack(writer, this.myValue); + return true; + } + + writer.Write(syncVarDirtyBits, 1); + if ((syncVarDirtyBits & 1UL) != 0UL) + { + myValue__Packer.Pack(writer, this.myValue); + result = true; + } + + return result; + } + + public override void DeserializeSyncVars(NetworkReader reader, bool initialState) + { + base.DeserializeSyncVars(reader, initialState); + + if (initialState) + { + this.myValue = myValue__Packer.Unpack(reader); + return; + } + + ulong dirtyMask = reader.Read(1); + if ((dirtyMask & 1UL) != 0UL) + { + this.myValue = myValue__Packer.Unpack(reader); + } + } + // CodeEmbed-End: float-pack-generated-code + } +} diff --git a/Assets/Mirage/Samples~/Snippets/BitPacking/FloatPackSnippets.cs.meta b/Assets/Mirage/Samples~/Snippets/BitPacking/FloatPackSnippets.cs.meta new file mode 100644 index 00000000000..be311138835 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/BitPacking/FloatPackSnippets.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2557e8e77af65c24ca16f9f6d24db8c0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/BitPacking/QuaternionPackSnippets.cs b/Assets/Mirage/Samples~/Snippets/BitPacking/QuaternionPackSnippets.cs new file mode 100644 index 00000000000..cac8879e48c --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/BitPacking/QuaternionPackSnippets.cs @@ -0,0 +1,65 @@ +using Mirage; +using Mirage.Serialization; +using UnityEngine; + +namespace Mirage.Snippets.BitPacking.QuaternionPackExample1 +{ + // CodeEmbed-Start: quaternion-pack-example-1 + public class MyNetworkBehaviour : NetworkBehaviour + { + [SyncVar, QuaternionPack(9)] + public Quaternion direction { get; set; } + } + // CodeEmbed-End: quaternion-pack-example-1 +} + +namespace Mirage.Snippets.BitPacking.QuaternionPackGenerated +{ + public class GeneratedExample : NetworkBehaviour + { + // CodeEmbed-Start: quaternion-pack-generated-source + [SyncVar, QuaternionPack(9)] + public Quaternion myValue { get; set; } + // CodeEmbed-End: quaternion-pack-generated-source + + // CodeEmbed-Start: quaternion-pack-generated-code + private QuaternionPacker myValue__Packer = new QuaternionPacker(9); + + public override bool SerializeSyncVars(NetworkWriter writer, bool initialState) + { + ulong syncVarDirtyBits = base.SyncVarDirtyBits; + bool result = base.SerializeSyncVars(writer, initialState); + + if (initialState) + { + myValue__Packer.Pack(writer, this.myValue); + return true; + } + + writer.Write(syncVarDirtyBits, 1); + if ((syncVarDirtyBits & 1UL) != 0UL) + { + myValue__Packer.Pack(writer, this.myValue); + result = true; + } + + return result; + } + + public override void DeserializeSyncVars(NetworkReader reader, bool initialState) + { + base.DeserializeSyncVars(reader, initialState); + + if (initialState) + { + this.myValue = myValue__Packer.Unpack(reader); + return; + } + + ulong dirtyMask = reader.Read(1); + if ((dirtyMask & 1UL) != 0UL) + this.myValue = myValue__Packer.Unpack(reader); + } + // CodeEmbed-End: quaternion-pack-generated-code + } +} diff --git a/Assets/Mirage/Samples~/Snippets/BitPacking/QuaternionPackSnippets.cs.meta b/Assets/Mirage/Samples~/Snippets/BitPacking/QuaternionPackSnippets.cs.meta new file mode 100644 index 00000000000..52114a47e14 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/BitPacking/QuaternionPackSnippets.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bd39de223f21a404bb3c34518250c13c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/BitPacking/VarIntBlocksSnippets.cs b/Assets/Mirage/Samples~/Snippets/BitPacking/VarIntBlocksSnippets.cs new file mode 100644 index 00000000000..d4cfc6c7bda --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/BitPacking/VarIntBlocksSnippets.cs @@ -0,0 +1,80 @@ +using Mirage; +using Mirage.Serialization; + +namespace Mirage.Snippets.BitPacking.VarIntBlocksExample1 +{ + // CodeEmbed-Start: var-int-blocks-example-1 + public class MyNetworkBehaviour : NetworkBehaviour + { + [SyncVar, VarIntBlocks(6)] + public int modifier { get; set; } + } + // CodeEmbed-End: var-int-blocks-example-1 +} + +namespace Mirage.Snippets.BitPacking.VarIntBlocksExample2 +{ + // CodeEmbed-Start: var-int-blocks-example-2 + public enum MyDirection + { + Backwards = -1, + None = 0, + Forwards = 1, + } + + public class MyNetworkBehaviour : NetworkBehaviour + { + [SyncVar, VarIntBlocks(2)] + public MyDirection direction { get; set; } + } + // CodeEmbed-End: var-int-blocks-example-2 +} + +namespace Mirage.Snippets.BitPacking.VarIntBlocksGenerated +{ + public class GeneratedExample : NetworkBehaviour + { + // CodeEmbed-Start: var-int-blocks-generated-source + [SyncVar, VarIntBlocks(6)] + public int myValue { get; set; } + // CodeEmbed-End: var-int-blocks-generated-source + + // CodeEmbed-Start: var-int-blocks-generated-code + public override bool SerializeSyncVars(NetworkWriter writer, bool initialState) + { + ulong syncVarDirtyBits = base.SyncVarDirtyBits; + bool result = base.SerializeSyncVars(writer, initialState); + + if (initialState) + { + VarIntBlocksPacker.Pack(writer, (ulong)this.myValue, 6); + return true; + } + + writer.Write(syncVarDirtyBits, 1); + if ((syncVarDirtyBits & 1UL) != 0UL) + { + VarIntBlocksPacker.Pack(writer, (ulong)this.myValue, 6); + result = true; + } + + return result; + } + + public override void DeserializeSyncVars(NetworkReader reader, bool initialState) + { + base.DeserializeSyncVars(reader, initialState); + + if (initialState) + { + this.myValue = (int)VarIntBlocksPacker.Unpack(reader, 6); + return; + } + + ulong dirtyMask = reader.Read(1); + if ((dirtyMask & 1UL) != 0UL) + this.myValue = (int)VarIntBlocksPacker.Unpack(reader, 6); + } + // CodeEmbed-End: var-int-blocks-generated-code + } +} diff --git a/Assets/Mirage/Samples~/Snippets/BitPacking/VarIntBlocksSnippets.cs.meta b/Assets/Mirage/Samples~/Snippets/BitPacking/VarIntBlocksSnippets.cs.meta new file mode 100644 index 00000000000..3bf1683fb8d --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/BitPacking/VarIntBlocksSnippets.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4fd72a47e2d7a1c448defdab834a3633 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/BitPacking/VectorPackSnippets.cs b/Assets/Mirage/Samples~/Snippets/BitPacking/VectorPackSnippets.cs new file mode 100644 index 00000000000..bdae999a029 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/BitPacking/VectorPackSnippets.cs @@ -0,0 +1,100 @@ +using Mirage; +using Mirage.Serialization; +using UnityEngine; + +namespace Mirage.Snippets.BitPacking.VectorPackExample1 +{ + // CodeEmbed-Start: vector-pack-example-1 + public class MyNetworkBehaviour : NetworkBehaviour + { + [SyncVar, Vector3Pack(100f, 100f, 100f, 0.05f)] + public Vector3 Position { get; set; } + } + // CodeEmbed-End: vector-pack-example-1 +} + +namespace Mirage.Snippets.BitPacking.VectorPackExample2 +{ + // CodeEmbed-Start: vector-pack-example-2 + public class MyNetworkBehaviour : NetworkBehaviour + { + [SyncVar, Vector3Pack(100f, 20f, 100f, 0.05f, 0.1f, 0.05f)] + public Vector3 Position { get; set; } + } + // CodeEmbed-End: vector-pack-example-2 +} + +namespace Mirage.Snippets.BitPacking.VectorPackExample3 +{ + // CodeEmbed-Start: vector-pack-example-3 + public class MyNetworkBehaviour : NetworkBehaviour + { + [SyncVar, Vector2Pack(1000f, 80f, 0.05f)] + public Vector2 Position { get; set; } + } + // CodeEmbed-End: vector-pack-example-3 +} + +namespace Mirage.Snippets.BitPacking.VectorPackGenerated +{ + public class GeneratedExample : NetworkBehaviour + { + // CodeEmbed-Start: vector-pack-generated-source + [SyncVar, Vector3Pack(100f, 20f, 100f, 0.05f, 0.1f, 0.05f)] + public Vector3 myValue1 { get; set; } + + [SyncVar, Vector2Pack(1000f, 80f, 0.05f)] + public Vector2 myValue2 { get; set; } + // CodeEmbed-End: vector-pack-generated-source + + // CodeEmbed-Start: vector-pack-generated-code + private Vector3Packer myValue1__Packer = new Vector3Packer(100f, 20f, 100f, 0.05f, 0.1f, 0.05f); + private Vector2Packer myValue2__Packer = new Vector2Packer(1000f, 80f, 0.05f, 0.05f); + + public override bool SerializeSyncVars(NetworkWriter writer, bool initialState) + { + ulong syncVarDirtyBits = base.SyncVarDirtyBits; + bool result = base.SerializeSyncVars(writer, initialState); + + if (initialState) + { + myValue1__Packer.Pack(writer, this.myValue1); + myValue2__Packer.Pack(writer, this.myValue2); + return true; + } + + writer.Write(syncVarDirtyBits, 2); + if ((syncVarDirtyBits & 1UL) != 0UL) + { + myValue1__Packer.Pack(writer, this.myValue1); + result = true; + } + if ((syncVarDirtyBits & 2UL) != 0UL) + { + myValue2__Packer.Pack(writer, this.myValue2); + result = true; + } + + return result; + } + + public override void DeserializeSyncVars(NetworkReader reader, bool initialState) + { + base.DeserializeSyncVars(reader, initialState); + + if (initialState) + { + this.myValue1 = myValue1__Packer.Unpack(reader); + this.myValue2 = myValue2__Packer.Unpack(reader); + return; + } + + ulong dirtyMask = reader.Read(2); + if ((dirtyMask & 1UL) != 0UL) + this.myValue1 = myValue1__Packer.Unpack(reader); + if ((dirtyMask & 2UL) != 0UL) + this.myValue2 = myValue2__Packer.Unpack(reader); + } + // CodeEmbed-End: vector-pack-generated-code + } +} diff --git a/Assets/Mirage/Samples~/Snippets/BitPacking/VectorPackSnippets.cs.meta b/Assets/Mirage/Samples~/Snippets/BitPacking/VectorPackSnippets.cs.meta new file mode 100644 index 00000000000..16294f00ecf --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/BitPacking/VectorPackSnippets.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7118a11f4feb2354f8bcc2707b06b35a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/BitPacking/ZigZagEncodeSnippets.cs b/Assets/Mirage/Samples~/Snippets/BitPacking/ZigZagEncodeSnippets.cs new file mode 100644 index 00000000000..efc21ebc0fe --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/BitPacking/ZigZagEncodeSnippets.cs @@ -0,0 +1,62 @@ +using Mirage; +using Mirage.Serialization; + +namespace Mirage.Snippets.BitPacking.ZigZagEncodeExample1 +{ + // CodeEmbed-Start: zig-zag-encode-example-1 + public class MyNetworkBehaviour : NetworkBehaviour + { + [SyncVar, BitCount(8), ZigZagEncode] + public int modifier { get; set; } + } + // CodeEmbed-End: zig-zag-encode-example-1 +} + +namespace Mirage.Snippets.BitPacking.ZigZagEncodeGenerated +{ + public class GeneratedExample : NetworkBehaviour + { + // CodeEmbed-Start: zig-zag-encode-generated-source + [SyncVar, BitCount(8), ZigZagEncode] + public int myValue { get; set; } + // CodeEmbed-End: zig-zag-encode-generated-source + + // CodeEmbed-Start: zig-zag-encode-generated-code + public override bool SerializeSyncVars(NetworkWriter writer, bool initialState) + { + ulong syncVarDirtyBits = base.SyncVarDirtyBits; + bool result = base.SerializeSyncVars(writer, initialState); + + if (initialState) + { + writer.Write((ulong)ZigZag.Encode(this.myValue), 8); + return true; + } + + writer.Write(syncVarDirtyBits, 1); + if ((syncVarDirtyBits & 1UL) != 0UL) + { + writer.Write((ulong)ZigZag.Encode(this.myValue), 8); + result = true; + } + + return result; + } + + public override void DeserializeSyncVars(NetworkReader reader, bool initialState) + { + base.DeserializeSyncVars(reader, initialState); + + if (initialState) + { + this.myValue = (int)ZigZag.Decode(reader.Read(8)); + return; + } + + ulong dirtyMask = reader.Read(1); + if ((dirtyMask & 1UL) != 0UL) + this.myValue = (int)ZigZag.Decode(reader.Read(8)); + } + // CodeEmbed-End: zig-zag-encode-generated-code + } +} diff --git a/Assets/Mirage/Samples~/Snippets/BitPacking/ZigZagEncodeSnippets.cs.meta b/Assets/Mirage/Samples~/Snippets/BitPacking/ZigZagEncodeSnippets.cs.meta new file mode 100644 index 00000000000..3f1fac2ce28 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/BitPacking/ZigZagEncodeSnippets.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ee1efce93f19d4d4a8b9b1318bbf1aef +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Callbacks.meta b/Assets/Mirage/Samples~/Snippets/Callbacks.meta new file mode 100644 index 00000000000..6e07d72df37 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Callbacks.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f2bd336cc762b51479c1e295afe2ee26 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Callbacks/NetworkBehaviourCallbacks.cs b/Assets/Mirage/Samples~/Snippets/Callbacks/NetworkBehaviourCallbacks.cs new file mode 100644 index 00000000000..538a2911050 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Callbacks/NetworkBehaviourCallbacks.cs @@ -0,0 +1,31 @@ +using Mirage; + +namespace Mirage.Snippets.Callbacks +{ + public class NetworkBehaviourCallbacks : NetworkBehaviour + { + // CodeEmbed-Start: network-behaviour-callbacks + void Awake() + { + Identity.OnStartServer.AddListener(MyStartServer); + Identity.OnStartClient.AddListener(MyStartClient); + Identity.OnStartLocalPlayer.AddListener(MyStartLocalPlayer); + } + + void MyStartServer() + { + // ... + } + + void MyStartClient() + { + // ... + } + + void MyStartLocalPlayer() + { + // ... + } + // CodeEmbed-End: network-behaviour-callbacks + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Callbacks/NetworkBehaviourCallbacks.cs.meta b/Assets/Mirage/Samples~/Snippets/Callbacks/NetworkBehaviourCallbacks.cs.meta new file mode 100644 index 00000000000..eaae46ffc8a --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Callbacks/NetworkBehaviourCallbacks.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4d95820dcd26bd244a97dc2778bfedb1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/CommunityGuides.meta b/Assets/Mirage/Samples~/Snippets/CommunityGuides.meta new file mode 100644 index 00000000000..1c7ee476fcc --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/CommunityGuides.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6337351a336f1b7419401ee9d2d1ac64 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/CommunityGuides/QuickStartGuide.cs b/Assets/Mirage/Samples~/Snippets/CommunityGuides/QuickStartGuide.cs new file mode 100644 index 00000000000..3a0127cf0f1 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/CommunityGuides/QuickStartGuide.cs @@ -0,0 +1,292 @@ +using Mirage; +using UnityEngine; +using UnityEngine.UI; + +namespace Mirage.Snippets.CommunityGuides +{ + namespace GettingStarted + { + // CodeEmbed-Start: quickstart-playerscript-base + public class PlayerScript : NetworkBehaviour + { + private void Awake() + { + Identity.OnStartLocalPlayer.AddListener(OnStartLocalPlayer); + } + + private void OnStartLocalPlayer() + { + Camera.main.transform.SetParent(transform); + Camera.main.transform.localPosition = new Vector3(0, 0, 0); + } + + private void Update() + { + if (!IsLocalPlayer) + return; + + float moveX = Input.GetAxis("Horizontal") * Time.deltaTime * 110.0f; + float moveZ = Input.GetAxis("Vertical") * Time.deltaTime * 4f; + + transform.Rotate(0, moveX, 0); + transform.Translate(0, 0, moveZ); + } + } + // CodeEmbed-End: quickstart-playerscript-base + + // CodeEmbed-Start: quickstart-startserver + public class StartServer : MonoBehaviour + { + [SerializeField] private NetworkManager networkManager; + + private void Start() + { + if (!networkManager) + return; + + networkManager.Server.StartServer(networkManager.Client); + } + } + // CodeEmbed-End: quickstart-startserver + } + + namespace QuickStart + { + public class SceneScript : NetworkBehaviour + { + public Text canvasStatusText; + public PlayerScript playerScript; + + [SyncVar(hook = nameof(OnStatusTextChanged))] + public string statusText { get; set; } + + void OnStatusTextChanged(string _Old, string _New) + { + //called from sync var hook, to update info on screen for all players + canvasStatusText.text = statusText; + } + + public void ButtonSendMessage() + { + if (playerScript != null) + { + playerScript.CmdSendPlayerMessage(); + } + } + } + + public class PlayerScript : NetworkBehaviour + { + // CodeEmbed-Start: quickstart-playerscript-names + public TextMesh playerNameText; + public GameObject floatingInfo; + + private Material playerMaterialClone; + + [SyncVar(hook = nameof(OnNameChanged))] + public string playerName { get; set; } + + [SyncVar(hook = nameof(OnColorChanged))] + public Color playerColor { get; set; } = Color.white; + + [ServerRpc] + public void CmdSetupPlayer(string _name, Color _col) + { + // player info sent to server, then server updates sync vars which handles it on all clients + playerName = _name; + playerColor = _col; + } + + private void Awake() + { + Identity.OnStartLocalPlayer.AddListener(OnStartLocalPlayer); + } + + private void OnStartLocalPlayer() + { + Camera.main.transform.SetParent(transform); + Camera.main.transform.localPosition = new Vector3(0, 0, 0); + + floatingInfo.transform.localPosition = new Vector3(0, -0.3f, 0.6f); + floatingInfo.transform.localScale = new Vector3(0.1f, 0.1f, 0.1f); + + string name = "Player" + Random.Range(100, 999); + Color color = new Color(Random.Range(0f, 1f), Random.Range(0f, 1f), Random.Range(0f, 1f)); + CmdSetupPlayer(name, color); + } + + private void OnNameChanged(string _Old, string _New) + { + playerNameText.text = playerName; + } + + private void OnColorChanged(Color _Old, Color _New) + { + playerNameText.color = _New; + playerMaterialClone = new Material(GetComponent().material); + playerMaterialClone.color = _New; + GetComponent().material = playerMaterialClone; + } + + private void Update() + { + if (!IsLocalPlayer) + { + // make non-local players run this + floatingInfo.transform.LookAt(Camera.main.transform); + return; + } + + float moveX = Input.GetAxis("Horizontal") * Time.deltaTime * 110.0f; + float moveZ = Input.GetAxis("Vertical") * Time.deltaTime * 4f; + + transform.Rotate(0, moveX, 0); + transform.Translate(0, 0, moveZ); + } + // CodeEmbed-End: quickstart-playerscript-names + + public void CmdSendPlayerMessage() {} + } + + public class PlayerScriptPart11 : NetworkBehaviour + { + public string playerName { get; set; } + public Color playerColor { get; set; } + + // CodeEmbed-Start: quickstart-playerscript-part11 + private SceneScript sceneScript; + + void Awake() + { + //allow all players to run this + sceneScript = GameObject.FindObjectOfType(); + Identity.OnStartLocalPlayer.AddListener(OnStartLocalPlayer); + } + [ServerRpc] + public void CmdSendPlayerMessage() + { + if (sceneScript) + { + sceneScript.statusText = $"{playerName} says hello {Random.Range(10, 99)}"; + } + } + [ServerRpc] + public void CmdSetupPlayer(string _name, Color _col) + { + //player info sent to server, then server updates sync vars which handles it on all clients + playerName = _name; + playerColor = _col; + sceneScript.statusText = $"{playerName} joined."; + } + public void OnStartLocalPlayer() + { + sceneScript.playerScript = this; + //. . . . ^ new line to add here + // CodeEmbed-End: quickstart-playerscript-part11 + } + } + + // CodeEmbed-Start: quickstart-scenescript + public class SceneScriptSnippet : NetworkBehaviour + { + public Text canvasStatusText; + public PlayerScript playerScript; + + [SyncVar(hook = nameof(OnStatusTextChanged))] + public string statusText { get; set; } + + void OnStatusTextChanged(string _Old, string _New) + { + //called from sync var hook, to update info on screen for all players + canvasStatusText.text = statusText; + } + + public void ButtonSendMessage() + { + if (playerScript != null) + { + playerScript.CmdSendPlayerMessage(); + } + } + } + // CodeEmbed-End: quickstart-scenescript + + public class PlayerScriptWeaponSwitch : NetworkBehaviour + { + public GameObject floatingInfo; + + // CodeEmbed-Start: quickstart-playerscript-weaponswitch + private int selectedWeaponLocal = 1; + public GameObject[] weaponArray; + + [SyncVar(hook = nameof(OnWeaponChanged))] + public int activeWeaponSynced { get; set; } + + void OnWeaponChanged(int _Old, int _New) + { + // disable old weapon + // in range and not null + if (0 < _Old && _Old < weaponArray.Length && weaponArray[_Old] != null) + { + weaponArray[_Old].SetActive(false); + } + + // enable new weapon + // in range and not null + if (0 < _New && _New < weaponArray.Length && weaponArray[_New] != null) + { + weaponArray[_New].SetActive(true); + } + } + + [ServerRpc] + public void CmdChangeActiveWeapon(int newIndex) + { + activeWeaponSynced = newIndex; + } + + void Awake() + { + // disable all weapons + foreach (var item in weaponArray) + { + if (item != null) + { + item.SetActive(false); + } + } + } + // CodeEmbed-End: quickstart-playerscript-weaponswitch + + // CodeEmbed-Start: quickstart-playerscript-weaponswitch-update + void Update() + { + if (!IsLocalPlayer) + { + // make non-local players run this + floatingInfo.transform.LookAt(Camera.main.transform); + return; + } + + float moveX = Input.GetAxis("Horizontal") * Time.deltaTime * 110.0f; + float moveZ = Input.GetAxis("Vertical") * Time.deltaTime * 4f; + + transform.Rotate(0, moveX, 0); + transform.Translate(0, 0, moveZ); + + if (Input.GetButtonDown("Fire2")) //Fire2 is mouse 2nd click and left alt + { + selectedWeaponLocal += 1; + + if (selectedWeaponLocal > weaponArray.Length) + { + selectedWeaponLocal = 1; + } + + CmdChangeActiveWeapon(selectedWeaponLocal); + } + } + // CodeEmbed-End: quickstart-playerscript-weaponswitch-update + } + } +} diff --git a/Assets/Mirage/Samples~/Snippets/CommunityGuides/QuickStartGuide.cs.meta b/Assets/Mirage/Samples~/Snippets/CommunityGuides/QuickStartGuide.cs.meta new file mode 100644 index 00000000000..2ad967885fe --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/CommunityGuides/QuickStartGuide.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fa51a8d87fbf95b4f83b6f6962eb308e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Components.meta b/Assets/Mirage/Samples~/Snippets/Components.meta new file mode 100644 index 00000000000..8d596ae8c9e --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Components.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d81da99aae4f0ca49ab9e35f69fc8c59 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/LobbyReadyCheck.cs b/Assets/Mirage/Samples~/Snippets/Components/LobbyReadyCheck.cs similarity index 97% rename from Assets/Mirage/Samples~/Snippets/LobbyReadyCheck.cs rename to Assets/Mirage/Samples~/Snippets/Components/LobbyReadyCheck.cs index c187fb4a070..f95df134e5f 100644 --- a/Assets/Mirage/Samples~/Snippets/LobbyReadyCheck.cs +++ b/Assets/Mirage/Samples~/Snippets/Components/LobbyReadyCheck.cs @@ -3,7 +3,7 @@ using UnityEngine; using UnityEngine.UI; -namespace Mirage.Snippets.LobbyReadyCheck +namespace Mirage.Snippets.Components { // CodeEmbed-Start: send-to-ready [NetworkMessage] diff --git a/Assets/Mirage/Samples~/Snippets/LobbyReadyCheck.cs.meta b/Assets/Mirage/Samples~/Snippets/Components/LobbyReadyCheck.cs.meta similarity index 83% rename from Assets/Mirage/Samples~/Snippets/LobbyReadyCheck.cs.meta rename to Assets/Mirage/Samples~/Snippets/Components/LobbyReadyCheck.cs.meta index d31fde58c22..b737529e008 100644 --- a/Assets/Mirage/Samples~/Snippets/LobbyReadyCheck.cs.meta +++ b/Assets/Mirage/Samples~/Snippets/Components/LobbyReadyCheck.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: aa36d87391bfff748ba8a611d38c048c +guid: f6ee138b8965ef74a9212cb25a481324 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/Mirage/Samples~/Snippets/Components/NetworkDiscoverySnippet.cs b/Assets/Mirage/Samples~/Snippets/Components/NetworkDiscoverySnippet.cs new file mode 100644 index 00000000000..4290903de5c --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Components/NetworkDiscoverySnippet.cs @@ -0,0 +1,73 @@ +using System; +using System.Net; + +namespace Mirage.Snippets.Components +{ + // Mock class to make the snippet compile/be structurally valid + public class NetworkDiscoveryBase + { + protected virtual void ProcessClientRequest(TRequest request, IPEndPoint endpoint) { } + protected virtual TResponse ProcessRequest(TRequest request, IPEndPoint endpoint) => default; + protected virtual TRequest GetRequest() => default; + protected virtual void ProcessResponse(TResponse response, IPEndPoint endpoint) { } + } + + // CodeEmbed-Start: discovery-messages + public class DiscoveryRequest + { + public string language = "en"; + + // Add properties for whatever information you want sent by clients + // in their broadcast messages that servers will consume. + } + + public class DiscoveryResponse + { + public enum GameMode { PvP, PvE } + + // you probably want uri so clients know how to connect to the server + public Uri uri; + + public GameMode GameMode; + public int TotalPlayers; + public int HostPlayerName; + + // Add properties for whatever information you want the server to return to + // clients for them to display or consume for establishing a connection. + } + // CodeEmbed-End: discovery-messages + + // CodeEmbed-Start: discovery-custom + public class NewNetworkDiscovery : NetworkDiscoveryBase + { + #region Server + + protected override void ProcessClientRequest(DiscoveryRequest request, IPEndPoint endpoint) + { + base.ProcessClientRequest(request, endpoint); + } + + protected override DiscoveryResponse ProcessRequest(DiscoveryRequest request, IPEndPoint endpoint) + { + // TODO: Create your response and return it + return new DiscoveryResponse(); + } + + #endregion + + #region Client + + protected override DiscoveryRequest GetRequest() + { + return new DiscoveryRequest(); + } + + protected override void ProcessResponse(DiscoveryResponse response, IPEndPoint endpoint) + { + // TODO: a server replied, do something with the response such as invoking a unityevent + } + + #endregion + } + // CodeEmbed-End: discovery-custom +} diff --git a/Assets/Mirage/Samples~/Snippets/Components/NetworkDiscoverySnippet.cs.meta b/Assets/Mirage/Samples~/Snippets/Components/NetworkDiscoverySnippet.cs.meta new file mode 100644 index 00000000000..141c671d0b9 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Components/NetworkDiscoverySnippet.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5c29b1074d5ac30498b71296af65e0c4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Components/NetworkLogSettingsSnippet.cs b/Assets/Mirage/Samples~/Snippets/Components/NetworkLogSettingsSnippet.cs new file mode 100644 index 00000000000..21306beca62 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Components/NetworkLogSettingsSnippet.cs @@ -0,0 +1,45 @@ +using Mirage.Logging; +using UnityEngine; + +namespace Mirage.Snippets.Components +{ + // CodeEmbed-Start: custom-log-setup + public class CustomLogSetup : MonoBehaviour + { + void Awake() + { + // Create default settings for MirageLogHandler + var settings = new MirageLogHandler.Settings( + timePrefix: MirageLogHandler.TimePrefix.DateTimeMilliSeconds, + coloredLabel: true, + label: true + ); + + // Replace the default log handler with MirageLogHandler + // This will apply to all existing and future loggers + LogFactory.ReplaceLogHandler((loggerName) => new MirageLogHandler(settings, loggerName)); + } + } + // CodeEmbed-End: custom-log-setup + + // CodeEmbed-Start: my-game-manager + public class MyGameManager : MonoBehaviour + { + // Obtain a logger for this class. + // The LogFactory will automatically manage its log level based on your LogSettingsSO. + private static readonly ILogger logger = LogFactory.GetLogger(); + + void Start() + { + // Example usage of the logger + logger.Log("MyGameManager started!"); + logger.LogWarning("Something might be wrong here."); + logger.LogError("Critical error occurred!"); + + // You can also check if a log type is enabled before logging to avoid unnecessary string formatting + if (logger.LogEnabled()) + logger.Log($"Current time: {Time.time}"); + } + } + // CodeEmbed-End: my-game-manager +} diff --git a/Assets/Mirage/Samples~/Snippets/Components/NetworkLogSettingsSnippet.cs.meta b/Assets/Mirage/Samples~/Snippets/Components/NetworkLogSettingsSnippet.cs.meta new file mode 100644 index 00000000000..9b79c86c208 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Components/NetworkLogSettingsSnippet.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eca8e6cbb8e426d468ae3a6b08738b46 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Components/NetworkSceneCheckerSnippet.cs b/Assets/Mirage/Samples~/Snippets/Components/NetworkSceneCheckerSnippet.cs new file mode 100644 index 00000000000..6e488e26701 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Components/NetworkSceneCheckerSnippet.cs @@ -0,0 +1,60 @@ +using Mirage; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace Mirage.Snippets.Components +{ + // Mock structures to ensure snippet compiles + public enum SceneOperation + { + LoadAdditive, + UnloadAdditive + } + + [NetworkMessage] + public struct SceneMessage + { + public string sceneName; + public SceneOperation sceneOperation; + } + + public class NetworkSceneCheckerSnippet : NetworkBehaviour + { + public void LoadScene(string subScene) + { + // CodeEmbed-Start: load-scene-async + SceneManager.LoadSceneAsync(subScene, LoadSceneMode.Additive); + // CodeEmbed-End: load-scene-async + } + + public void SendSceneMessage(string subScene) + { + // CodeEmbed-Start: send-scene-message + SceneMessage msg = new SceneMessage + { + sceneName = subScene, + sceneOperation = SceneOperation.LoadAdditive + }; + + Owner.Send(msg); + // CodeEmbed-End: send-scene-message + } + + public void MovePlayerToScene(GameObject player, Scene subScene) + { + // CodeEmbed-Start: move-player-to-scene + // Get the SceneVisibilityChecker component + SceneVisibilityChecker sceneChecker = player.GetComponent(); + if (sceneChecker != null) + { + // Position the character object in world space first (if needed) + // This assumes it has a NetworkTransform component that will update clients + player.transform.position = new Vector3(100, 1, 100); + + // Then move the character object to the subscene using the checker's method + sceneChecker.MoveToScene(subScene); + } + // CodeEmbed-End: move-player-to-scene + } + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Components/NetworkSceneCheckerSnippet.cs.meta b/Assets/Mirage/Samples~/Snippets/Components/NetworkSceneCheckerSnippet.cs.meta new file mode 100644 index 00000000000..ec684c1f33f --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Components/NetworkSceneCheckerSnippet.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 13e88029934f71048b7de6052623edd7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Components/NetworkSceneManagerSnippet.cs b/Assets/Mirage/Samples~/Snippets/Components/NetworkSceneManagerSnippet.cs new file mode 100644 index 00000000000..7688fc73577 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Components/NetworkSceneManagerSnippet.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.SceneManagement; +using Mirage; + +namespace Mirage.Snippets.Components +{ + public class NetworkSceneManagerSnippet : MonoBehaviour + { + public void SceneChangeExamples(NetworkSceneManager sceneManager, IEnumerable players, Scene scene) + { + // CodeEmbed-Start: scene-change-examples + // For normal scene changes + sceneManager.ServerLoadSceneNormal("Assets/GameScene.unity"); + + // For additive scene loading + sceneManager.ServerLoadSceneAdditively("Assets/AdditiveScene.unity", players); + + // For additive scene unloading + sceneManager.ServerUnloadSceneAdditively(scene, players); + // CodeEmbed-End: scene-change-examples + } + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Components/NetworkSceneManagerSnippet.cs.meta b/Assets/Mirage/Samples~/Snippets/Components/NetworkSceneManagerSnippet.cs.meta new file mode 100644 index 00000000000..98c43811b3f --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Components/NetworkSceneManagerSnippet.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8e4dd8746a980644186b2e1df529c03f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects.meta b/Assets/Mirage/Samples~/Snippets/GameObjects.meta new file mode 100644 index 00000000000..5671b1f80c7 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 266f272c84b37044e9835dd9cb3f4636 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/CustomPlayerSpawning.cs b/Assets/Mirage/Samples~/Snippets/GameObjects/CustomPlayerSpawning.cs new file mode 100644 index 00000000000..02f84d9b00b --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/CustomPlayerSpawning.cs @@ -0,0 +1,163 @@ +using System.ComponentModel; +using Mirage; +using UnityEngine; + +namespace Mirage.Snippets.GameObjects +{ + // CodeEmbed-Start: create-mmo-character-message + public struct CreateMMOCharacterMessage + { + public Race race; + public string name; + public Color hairColor; + public Color eyeColor; + } + + public enum Race + { + Human, + Elvish, + Dwarvish, + } + // CodeEmbed-End: create-mmo-character-message + + // CodeEmbed-Start: custom-character-spawner-class + public class CustomCharacterSpawner : MonoBehaviour + { + [Header("References")] + public NetworkClient Client; + public NetworkServer Server; + public ClientObjectManager ClientObjectManager; + public ServerObjectManager ServerObjectManager; + + [Header("Prefabs")] + // Different prefabs based on the Race the player picks + public CustomCharacter HumanPrefab; + public CustomCharacter ElvishPrefab; + public CustomCharacter DwarvishPrefab; + // CodeEmbed-End: custom-character-spawner-class + + // CodeEmbed-Start: custom-character-spawner-start + public void Start() + { + Client.Started.AddListener(OnClientStarted); + Client.Authenticated.AddListener(OnClientAuthenticated); + Server.Started.AddListener(OnServerStarted); + } + // CodeEmbed-End: custom-character-spawner-start + + // CodeEmbed-Start: custom-character-spawner-client-started + private void OnClientStarted() + { + // Make sure all prefabs are Register so mirage can spawn the character for this client and for other players + ClientObjectManager.RegisterPrefab(HumanPrefab.Identity); + ClientObjectManager.RegisterPrefab(ElvishPrefab.Identity); + ClientObjectManager.RegisterPrefab(DwarvishPrefab.Identity); + } + // CodeEmbed-End: custom-character-spawner-client-started + + // CodeEmbed-Start: custom-character-spawner-client-authenticated + // You can send the message here if you already know + // everything about the character at the time of player + // or at a later time when the user submits his preferences + private void OnClientAuthenticated(INetworkPlayer player) + { + var mmoCharacter = new CreateMMOCharacterMessage + { + // populate the message with your data + name = "player user name", + race = Race.Human, + eyeColor = Color.red, + hairColor = Color.black, + }; + player.Send(mmoCharacter); + } + // CodeEmbed-End: custom-character-spawner-client-authenticated + + // CodeEmbed-Start: custom-character-spawner-server-started + private void OnServerStarted() + { + // Wait for client to send us an AddPlayerMessage + Server.MessageHandler.RegisterHandler(OnCreateCharacter); + } + + private void OnCreateCharacter(INetworkPlayer player, CreateMMOCharacterMessage msg) + { + CustomCharacter prefab = GetPrefab(msg); + + // Create your character object + // Use the data in msg to configure it + CustomCharacter character = Instantiate(prefab); + + // Set syncVars before telling Mirage to spawn character + // This will cause them to be sent to client in the spawn message + character.PlayerName = msg.name; + character.hairColor = msg.hairColor; + character.eyeColor = msg.eyeColor; + + // Spawn it as the character object + ServerObjectManager.AddCharacter(player, character.Identity); + } + + private CustomCharacter GetPrefab(CreateMMOCharacterMessage msg) + { + // Get prefab based on race + CustomCharacter prefab; + switch (msg.race) + { + case Race.Human: prefab = HumanPrefab; break; + case Race.Elvish: prefab = ElvishPrefab; break; + case Race.Dwarvish: prefab = DwarvishPrefab; break; + // Default case to check that client sent valid race. + // The only reason it should be invalid is if the client's code was modified by an attacker + // Throw will cause the client to be kicked + default: throw new InvalidEnumArgumentException("Invalid race given"); + } + + return prefab; + } + // CodeEmbed-End: custom-character-spawner-server-started + } + + public class CustomCharacter : NetworkBehaviour + { + public string PlayerName { get; set; } + public Color hairColor { get; set; } + public Color eyeColor { get; set; } + } + + // CodeEmbed-Start: custom-character-spawner-respawn + public class CustomCharacterSpawnerRespawn : MonoBehaviour + { + public NetworkServer Server; + public ServerObjectManager ServerObjectManager; + + public void Respawn(NetworkPlayer player, GameObject newPrefab) + { + // Cache a reference to the current character object + GameObject oldPlayer = player.Identity.gameObject; + + var newCharacter = Instantiate(newPrefab); + + // Instantiate the new character object and broadcast to clients + // NOTE: here we can use `keepAuthority: true` because we are calling Destroy on the old prefab immediately after. + ServerObjectManager.ReplaceCharacter(player, newCharacter, keepAuthority: true); + + // Remove the previous character object that's now been replaced + Server.Destroy(oldPlayer); + } + } + // CodeEmbed-End: custom-character-spawner-respawn + + public class PlayerDeathHandler : MonoBehaviour + { + public ServerObjectManager ServerObjectManager; + + // CodeEmbed-Start: custom-character-spawner-death + public void OnPlayerDeath(INetworkPlayer player) + { + ServerObjectManager.DestroyCharacter(player); + } + // CodeEmbed-End: custom-character-spawner-death + } +} diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/CustomPlayerSpawning.cs.meta b/Assets/Mirage/Samples~/Snippets/GameObjects/CustomPlayerSpawning.cs.meta new file mode 100644 index 00000000000..89ad78d6bd6 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/CustomPlayerSpawning.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d7f8012670d625c40bc3ad589e3a0438 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/CustomSpawnExample.cs b/Assets/Mirage/Samples~/Snippets/GameObjects/CustomSpawnExample.cs new file mode 100644 index 00000000000..4cece8a294b --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/CustomSpawnExample.cs @@ -0,0 +1,104 @@ +using Mirage; +using UnityEngine; + +namespace Mirage.Snippets.GameObjects +{ + public class CustomSpawnExample : MonoBehaviour + { + public ClientObjectManager ClientObjectManager; + public NetworkServer NetworkServer; + public NetworkIdentity coin; + public NetworkIdentity m_CoinPrefab; + public NetworkIdentity prefab; + public ClientObjectManager clientObjectManager; + + // CodeEmbed-Start: spawn-handler-delegate + NetworkIdentity SpawnDelegate(SpawnMessage msg) + { + // do stuff here + return null; + } + // CodeEmbed-End: spawn-handler-delegate + + // CodeEmbed-Start: unspawn-handler-delegate + void UnSpawnDelegate(NetworkIdentity spawned) + { + // do stuff here + } + // CodeEmbed-End: unspawn-handler-delegate + + public void SetupHandlers() + { + // CodeEmbed-Start: generate-prefab-runtime + // Create a hash that can be generated on both server and client + // using a string and GetStableHashCode is a good way to do this + int coinHash = "MyCoin".GetStableHashCode(); + + // register handlers using hash + ClientObjectManager.RegisterSpawnHandler(coinHash, SpawnCoin, UnSpawnCoin); + // CodeEmbed-End: generate-prefab-runtime + + // CodeEmbed-Start: use-existing-prefab + // register handlers using prefab + ClientObjectManager.RegisterPrefab(coin, SpawnCoin, UnSpawnCoin); + // CodeEmbed-End: use-existing-prefab + } + + public void SpawnOnServer() + { + // CodeEmbed-Start: spawn-on-server + int coinHash = "MyCoin".GetStableHashCode(); + + // spawn a coin - SpawnCoin is called on client + // pass in coinHash so that it is set on the Identity before it is sent to client + NetworkServer.Spawn(gameObject, coinHash); + // CodeEmbed-End: spawn-on-server + } + + // CodeEmbed-Start: spawn-coin-methods + public NetworkIdentity SpawnCoin(SpawnMessage msg) + { + return Instantiate(m_CoinPrefab, msg.position, msg.rotation); + } + public void UnSpawnCoin(NetworkIdentity spawned) + { + Destroy(spawned); + } + // CodeEmbed-End: spawn-coin-methods + + // CodeEmbed-Start: pool-spawn-handlers + void ClientConnected() + { + clientObjectManager.RegisterPrefab(prefab, PoolSpawnHandler, PoolUnspawnHandler); + } + + // used by clientObjectManager.RegisterPrefab + NetworkIdentity PoolSpawnHandler(SpawnMessage msg) + { + return GetFromPool(msg.position, msg.rotation); + } + + // used by clientObjectManager.RegisterPrefab + void PoolUnspawnHandler(NetworkIdentity spawned) + { + PutBackInPool(spawned); + } + // CodeEmbed-End: pool-spawn-handlers + + private NetworkIdentity GetFromPool(Vector3 position, Quaternion rotation) + { + return null; + } + + private void PutBackInPool(NetworkIdentity spawned) + { + } + } + + public static class ClientObjectManagerExtensions + { + public static void RegisterPrefab(this ClientObjectManager manager, NetworkIdentity identity, SpawnHandlerDelegate spawnHandler, UnSpawnDelegate unspawnHandler) + { + } + } +} diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/CustomSpawnExample.cs.meta b/Assets/Mirage/Samples~/Snippets/GameObjects/CustomSpawnExample.cs.meta new file mode 100644 index 00000000000..f250a6cd1fa --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/CustomSpawnExample.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 103537568df758c429ab91d3cf40a638 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/LifecycleComponent.cs b/Assets/Mirage/Samples~/Snippets/GameObjects/LifecycleComponent.cs new file mode 100644 index 00000000000..99544828f27 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/LifecycleComponent.cs @@ -0,0 +1,19 @@ +using UnityEngine; + +namespace Mirage.Snippets.GameObjects +{ + // CodeEmbed-Start: lifecycle-start-server + public class LifecycleComponent : MonoBehaviour + { + public void Awake() + { + GetComponent().OnStartServer.AddListener(OnStartServer); + } + + public void OnStartServer() + { + Debug.Log("The object started on the server"); + } + } + // CodeEmbed-End: lifecycle-start-server +} diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/LifecycleComponent.cs.meta b/Assets/Mirage/Samples~/Snippets/GameObjects/LifecycleComponent.cs.meta new file mode 100644 index 00000000000..4cf752d98f6 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/LifecycleComponent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a01891b371b176047962b6005236d160 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/MyNetworkManager.cs b/Assets/Mirage/Samples~/Snippets/GameObjects/MyNetworkManager.cs new file mode 100644 index 00000000000..ad9ee75fe08 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/MyNetworkManager.cs @@ -0,0 +1,70 @@ +using UnityEngine; +using Mirage; + +namespace Mirage.Snippets.GameObjects +{ + // CodeEmbed-Start: spawning-without-network-manager-1 + public class MyNetworkManager : MonoBehaviour + { + public GameObject treePrefab; + public ClientObjectManager ClientObjectManager; + public NetworkClient NetworkClient; + public NetworkServer NetworkServer; + public ServerObjectManager ServerObjectManager; + + void Start() + { + ClientObjectManager = FindObjectOfType(); + NetworkClient = FindObjectOfType(); + NetworkServer = FindObjectOfType(); + ServerObjectManager = FindObjectOfType(); + } + + // Register prefab and connect to the server + public void ClientConnect() + { + ClientObjectManager.spawnPrefabs.Add(treePrefab); + NetworkClient.Connect("localhost"); + NetworkClient.MessageHandler.RegisterHandler(OnClientConnect); + } + + void OnClientConnect(NetworkConnection conn, ConnectMessage msg) + { + Debug.Log("Connected to server: " + conn); + } + // CodeEmbed-End: spawning-without-network-manager-1 + + // CodeEmbed-Start: spawning-without-network-manager-2 + public void ServerListen() + { + // start listening, and allow up to 4 connections + NetworkServer.StartServer(); + + NetworkServer.MessageHandler.RegisterHandler(OnServerConnect); + NetworkServer.MessageHandler.RegisterHandler(OnClientReady); + } + + // When client is ready spawn a few trees + void OnClientReady(NetworkConnection conn, ReadyMessage msg) + { + Debug.Log("Client is ready to start: " + conn); + SpawnTrees(); + } + + void SpawnTrees() + { + int x = 0; + for (int i = 0; i < 5; ++i) + { + GameObject treeGo = Instantiate(treePrefab, new Vector3(x++, 0, 0), Quaternion.identity); + ServerObjectManager.Spawn(treeGo); + } + } + + void OnServerConnect(NetworkConnection conn, ConnectMessage msg) + { + Debug.Log("New client connected: " + conn); + } + // CodeEmbed-End: spawning-without-network-manager-2 + } +} diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/MyNetworkManager.cs.meta b/Assets/Mirage/Samples~/Snippets/GameObjects/MyNetworkManager.cs.meta new file mode 100644 index 00000000000..1ec9e206f61 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/MyNetworkManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3b83559698cd05247a45886380f0b6d1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/NetworkWorldEvents.cs b/Assets/Mirage/Samples~/Snippets/GameObjects/NetworkWorldEvents.cs new file mode 100644 index 00000000000..fd728632fbe --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/NetworkWorldEvents.cs @@ -0,0 +1,52 @@ +using UnityEngine; + +namespace Mirage.Snippets.GameObjects +{ + // CodeEmbed-Start: network-world-events + public class NetworkWorldEvents : MonoBehaviour + { + public NetworkServer Server; + public NetworkClient Client; + + public void Awake() + { + // Client/Server.World is only set after server is started, + // so wait for start, then add event listener to OnSpawn + Server.Started.AddListener(ServerStarted); + Client.Started.AddListener(ClientStarted); + } + + private void ServerStarted() + { + Server.World.onSpawn += OnServerSpawn; + Server.World.onUnspawn += OnServerUnspawn; + } + + private void OnServerSpawn(NetworkIdentity identity) + { + Debug.Log($"The object {identity} was spawned on the server"); + } + + private void OnServerUnspawn(uint netId, NetworkIdentity identity) + { + Debug.Log($"The object {identity} (netId={netId}) was unspawned on the server"); + } + + private void ClientStarted() + { + Client.World.onSpawn += OnClientSpawn; + Client.World.onUnspawn += OnClientUnspawn; + } + + private void OnClientSpawn(NetworkIdentity identity) + { + Debug.Log($"The object {identity} was spawned on the client"); + } + + private void OnClientUnspawn(uint netId, NetworkIdentity identity) + { + Debug.Log($"The object {identity} (netId={netId}) was unspawned on the client"); + } + } + // CodeEmbed-End: network-world-events +} diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/NetworkWorldEvents.cs.meta b/Assets/Mirage/Samples~/Snippets/GameObjects/NetworkWorldEvents.cs.meta new file mode 100644 index 00000000000..8e247cb3881 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/NetworkWorldEvents.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1c81ac790270753498214023e0b3a480 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerCharacter.cs b/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerCharacter.cs new file mode 100644 index 00000000000..f97e1abbaee --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerCharacter.cs @@ -0,0 +1,18 @@ +using UnityEngine; + +namespace Mirage.Snippets.GameObjects +{ + // CodeEmbed-Start: player-proxy-character + public class PlayerCharacter : NetworkBehaviour + { + private void Update() + { + // use HasAuthority, not IsLocalPlayer + if (!HasAuthority) + return; + + // handle input and movement + } + } + // CodeEmbed-End: player-proxy-character +} diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerCharacter.cs.meta b/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerCharacter.cs.meta new file mode 100644 index 00000000000..0099b07104d --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerCharacter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5001744f74546b74d89df3bfe0f803ce +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerContext.cs b/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerContext.cs new file mode 100644 index 00000000000..99ee03a204c --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerContext.cs @@ -0,0 +1,15 @@ +using UnityEngine; + +namespace Mirage.Snippets.GameObjects +{ + // CodeEmbed-Start: player-proxy-context + public class PlayerContext : NetworkBehaviour + { + [SyncVar] public string PlayerName { get; set; } + [SyncVar] public string Team { get; set; } + + // easy access to the gameplay character via NetworkPlayer.Identity + [SyncVar] public PlayerCharacter ActiveCharacter { get; set; } + } + // CodeEmbed-End: player-proxy-context +} diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerContext.cs.meta b/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerContext.cs.meta new file mode 100644 index 00000000000..b99c76f1ec9 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bdfe768aff9d7a84aa5b17381c3a1578 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerEquip.cs b/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerEquip.cs new file mode 100644 index 00000000000..dea652fb965 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerEquip.cs @@ -0,0 +1,123 @@ +using System.Collections; +using UnityEngine; + +namespace Mirage.Snippets.GameObjects +{ + public enum EquippedItem : byte + { + nothing, + ball, + box, + cylinder + } + + public class PlayerEquip : NetworkBehaviour + { + public GameObject sceneObjectPrefab; + + public GameObject rightHand; + + public GameObject ballPrefab; + public GameObject boxPrefab; + public GameObject cylinderPrefab; + + [SyncVar(hook = nameof(OnChangeEquipment))] + public EquippedItem equippedItem { get; set; } + + private void OnChangeEquipment(EquippedItem oldEquippedItem, EquippedItem newEquippedItem) + { + StartCoroutine(ChangeEquipment(newEquippedItem)); + } + + // Since Destroy is delayed to the end of the current frame, we use a coroutine + // to clear out any child objects before instantiating the new one + private IEnumerator ChangeEquipment(EquippedItem newEquippedItem) + { + while (rightHand.transform.childCount > 0) + { + Destroy(rightHand.transform.GetChild(0).gameObject); + yield return null; + } + + switch (newEquippedItem) + { + case EquippedItem.ball: + Instantiate(ballPrefab, rightHand.transform); + break; + case EquippedItem.box: + Instantiate(boxPrefab, rightHand.transform); + break; + case EquippedItem.cylinder: + Instantiate(cylinderPrefab, rightHand.transform); + break; + } + } + + // CodeEmbed-Start: player-equip-update + private void Update() + { + if (!IsLocalPlayer) + return; + + if (Input.GetKeyDown(KeyCode.Alpha0) && equippedItem != EquippedItem.nothing) + CmdChangeEquippedItem(EquippedItem.nothing); + if (Input.GetKeyDown(KeyCode.Alpha1) && equippedItem != EquippedItem.ball) + CmdChangeEquippedItem(EquippedItem.ball); + if (Input.GetKeyDown(KeyCode.Alpha2) && equippedItem != EquippedItem.box) + CmdChangeEquippedItem(EquippedItem.box); + if (Input.GetKeyDown(KeyCode.Alpha3) && equippedItem != EquippedItem.cylinder) + CmdChangeEquippedItem(EquippedItem.cylinder); + + if (Input.GetKeyDown(KeyCode.X) && equippedItem != EquippedItem.nothing) + CmdDropItem(); + } + // CodeEmbed-End: player-equip-update + + // CodeEmbed-Start: player-equip-drop + [ServerRpc] + private void CmdDropItem() + { + // Instantiate the scene object on the server + Vector3 pos = rightHand.transform.position; + Quaternion rot = rightHand.transform.rotation; + GameObject newSceneObject = Instantiate(sceneObjectPrefab, pos, rot); + + // set the RigidBody as non-kinematic on the server only (isKinematic = true in prefab) + newSceneObject.GetComponent().isKinematic = false; + + SceneObject sceneObject = newSceneObject.GetComponent(); + + // set the child object on the server + sceneObject.SetEquippedItem(equippedItem); + + // set the SyncVar on the scene object for clients + sceneObject.equippedItem = equippedItem; + + // set the player's SyncVar to nothing so clients will destroy the equipped child item + equippedItem = EquippedItem.nothing; + + // Spawn the scene object on the network for all to see + ServerObjectManager.Spawn(newSceneObject); + } + // CodeEmbed-End: player-equip-drop + + [ServerRpc] + private void CmdChangeEquippedItem(EquippedItem selectedItem) + { + equippedItem = selectedItem; + } + + // CodeEmbed-Start: player-equip-pickup + // CmdPickupItem is public because it's called from a script on the SceneObject + [ServerRpc] + public void CmdPickupItem(GameObject sceneObject) + { + // set the player's SyncVar so clients can show the equipped item + equippedItem = sceneObject.GetComponent().equippedItem; + + // Destroy the scene object + ServerObjectManager.Destroy(sceneObject); + } + // CodeEmbed-End: player-equip-pickup + } +} diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerEquip.cs.meta b/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerEquip.cs.meta new file mode 100644 index 00000000000..dff40fdc1b5 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerEquip.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2c0f7c6bfd7b02c40b4338116d7ba43a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerEquipInitial.cs b/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerEquipInitial.cs new file mode 100644 index 00000000000..e7bf27f82d6 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerEquipInitial.cs @@ -0,0 +1,79 @@ +using System.Collections; +using UnityEngine; + +namespace Mirage.Snippets.GameObjects.Initial +{ + // CodeEmbed-Start: player-equip-initial + public enum EquippedItem : byte + { + nothing, + ball, + box, + cylinder + } + + public class PlayerEquip : NetworkBehaviour + { + public GameObject sceneObjectPrefab; + + public GameObject rightHand; + + public GameObject ballPrefab; + public GameObject boxPrefab; + public GameObject cylinderPrefab; + + [SyncVar(hook = nameof(OnChangeEquipment))] + public EquippedItem equippedItem { get; set; } + + private void OnChangeEquipment(EquippedItem oldEquippedItem, EquippedItem newEquippedItem) + { + StartCoroutine(ChangeEquipment(newEquippedItem)); + } + + // Since Destroy is delayed to the end of the current frame, we use a coroutine + // to clear out any child objects before instantiating the new one + private IEnumerator ChangeEquipment(EquippedItem newEquippedItem) + { + while (rightHand.transform.childCount > 0) + { + Destroy(rightHand.transform.GetChild(0).gameObject); + yield return null; + } + + switch (newEquippedItem) + { + case EquippedItem.ball: + Instantiate(ballPrefab, rightHand.transform); + break; + case EquippedItem.box: + Instantiate(boxPrefab, rightHand.transform); + break; + case EquippedItem.cylinder: + Instantiate(cylinderPrefab, rightHand.transform); + break; + } + } + + private void Update() + { + if (!IsLocalPlayer) + return; + + if (Input.GetKeyDown(KeyCode.Alpha0) && equippedItem != EquippedItem.nothing) + CmdChangeEquippedItem(EquippedItem.nothing); + if (Input.GetKeyDown(KeyCode.Alpha1) && equippedItem != EquippedItem.ball) + CmdChangeEquippedItem(EquippedItem.ball); + if (Input.GetKeyDown(KeyCode.Alpha2) && equippedItem != EquippedItem.box) + CmdChangeEquippedItem(EquippedItem.box); + if (Input.GetKeyDown(KeyCode.Alpha3) && equippedItem != EquippedItem.cylinder) + CmdChangeEquippedItem(EquippedItem.cylinder); + } + + [ServerRpc] + private void CmdChangeEquippedItem(EquippedItem selectedItem) + { + equippedItem = selectedItem; + } + } + // CodeEmbed-End: player-equip-initial +} diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerEquipInitial.cs.meta b/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerEquipInitial.cs.meta new file mode 100644 index 00000000000..900284a45b5 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerEquipInitial.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 475cdcaf510782f49ad8ecbd913367bf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerProxyManager.cs b/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerProxyManager.cs new file mode 100644 index 00000000000..54af1bf90be --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerProxyManager.cs @@ -0,0 +1,48 @@ +using UnityEngine; + +namespace Mirage.Snippets.GameObjects +{ + public class PlayerProxyManager : MonoBehaviour + { + public NetworkServer Server; + public ServerObjectManager ServerObjectManager; + public NetworkIdentity PlayerProxyPrefab; + public PlayerCharacter CharacterPrefab; + public Transform spawnPoint; + + // CodeEmbed-Start: player-proxy-spawn-proxy + private void OnServerAuthenticated(INetworkPlayer player) + { + var proxy = Instantiate(PlayerProxyPrefab); + ServerObjectManager.AddCharacter(player, proxy.gameObject); + } + // CodeEmbed-End: player-proxy-spawn-proxy + + // CodeEmbed-Start: player-proxy-spawn-character + private void SpawnGameplayCharacter(INetworkPlayer player) + { + var proxy = player.Identity.GetComponent(); + + var character = Instantiate(CharacterPrefab, spawnPoint.position, spawnPoint.rotation); + ServerObjectManager.Spawn(character.Identity, player); + + proxy.ActiveCharacter = character; + } + // CodeEmbed-End: player-proxy-spawn-character + + // CodeEmbed-Start: player-proxy-respawn + private void RespawnCharacter(INetworkPlayer player) + { + var proxy = player.Identity.GetComponent(); + + if (proxy.ActiveCharacter != null) + { + ServerObjectManager.Destroy(proxy.ActiveCharacter.gameObject); + proxy.ActiveCharacter = null; + } + + SpawnGameplayCharacter(player); + } + // CodeEmbed-End: player-proxy-respawn + } +} diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerProxyManager.cs.meta b/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerProxyManager.cs.meta new file mode 100644 index 00000000000..9112cf8ce10 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/PlayerProxyManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 23537eaa8734bb04c83103b7163040f5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/SceneObject.cs b/Assets/Mirage/Samples~/Snippets/GameObjects/SceneObject.cs new file mode 100644 index 00000000000..8eea1e51c5b --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/SceneObject.cs @@ -0,0 +1,64 @@ +using System.Collections; +using UnityEngine; + +namespace Mirage.Snippets.GameObjects +{ + // CodeEmbed-Start: scene-object-class + public class SceneObject : NetworkBehaviour + { + [SyncVar(hook = nameof(OnChangeEquipment))] + public EquippedItem equippedItem { get; set; } + + public GameObject ballPrefab; + public GameObject boxPrefab; + public GameObject cylinderPrefab; + + private void OnChangeEquipment(EquippedItem oldEquippedItem, EquippedItem newEquippedItem) + { + StartCoroutine(ChangeEquipment(newEquippedItem)); + } + + // Since Destroy is delayed to the end of the current frame, we use a coroutine + // to clear out any child objects before instantiating the new one + private IEnumerator ChangeEquipment(EquippedItem newEquippedItem) + { + while (transform.childCount > 0) + { + Destroy(transform.GetChild(0).gameObject); + yield return null; + } + + // Use the new value, not the SyncVar property value + SetEquippedItem(newEquippedItem); + } + + // SetEquippedItem is called on the client from OnChangeEquipment (above), + // and on the server from CmdDropItem in the PlayerEquip script. + public void SetEquippedItem(EquippedItem newEquippedItem) + { + switch (newEquippedItem) + { + case EquippedItem.ball: + Instantiate(ballPrefab, transform); + break; + case EquippedItem.box: + Instantiate(boxPrefab, transform); + break; + case EquippedItem.cylinder: + Instantiate(cylinderPrefab, transform); + break; + } + } + } + // CodeEmbed-End: scene-object-class + + public class SceneObjectClick : NetworkBehaviour + { + // CodeEmbed-Start: scene-object-mousedown + private void OnMouseDown() + { + Client.Player.Identity.GetComponent().CmdPickupItem(gameObject); + } + // CodeEmbed-End: scene-object-mousedown + } +} diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/SceneObject.cs.meta b/Assets/Mirage/Samples~/Snippets/GameObjects/SceneObject.cs.meta new file mode 100644 index 00000000000..4eef7cd713e --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/SceneObject.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4e8c62654c5152b43b35cc741b5578a7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/SceneObjectFilterExample.cs b/Assets/Mirage/Samples~/Snippets/GameObjects/SceneObjectFilterExample.cs new file mode 100644 index 00000000000..9c137c68612 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/SceneObjectFilterExample.cs @@ -0,0 +1,37 @@ +using Mirage; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace Mirage.Snippets.GameObjects +{ + // CodeEmbed-Start: scene-object-filter-example + public class MySceneManager : MonoBehaviour + { + public ServerObjectManager serverObjectManager; + public ClientObjectManager clientObjectManager; + public Scene myScene; + + // Set the scene to use for filtering + public void SetScene(Scene scene) + { + myScene = scene; + } + + void Awake() + { + // Set the filter before spawning scene objects + var filter = (NetworkIdentity identity) => + { + return identity.gameObject.scene == myScene; + }; + + serverObjectManager.SceneObjectFilter = filter; + clientObjectManager.SceneObjectFilter = filter; + + // Now when SpawnSceneObjects is called, it will only + // consider objects from `myScene`. + serverObjectManager.SpawnSceneObjects(); + } + } + // CodeEmbed-End: scene-object-filter-example +} diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/SceneObjectFilterExample.cs.meta b/Assets/Mirage/Samples~/Snippets/GameObjects/SceneObjectFilterExample.cs.meta new file mode 100644 index 00000000000..a0f7cf2ada0 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/SceneObjectFilterExample.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ef811c5b53418ce479e58787e6d59249 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/SpawningExample.cs b/Assets/Mirage/Samples~/Snippets/GameObjects/SpawningExample.cs new file mode 100644 index 00000000000..9e0df3e60c3 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/SpawningExample.cs @@ -0,0 +1,50 @@ +using Mirage; +using UnityEngine; + +namespace Mirage.Snippets.GameObjects +{ + public class SpawningExample : MonoBehaviour + { + public GameObject boxPrefab; + public ServerObjectManager ServerObjectManager; + public GameObject treePrefab; + + public void SpawnBox() + { + // CodeEmbed-Start: spawn-box-example + var boxGo = Instantiate(boxPrefab); + ServerObjectManager.Spawn(boxGo); + // CodeEmbed-End: spawn-box-example + } + + // CodeEmbed-Start: spawn-trees-example + void SpawnTrees() + { + int x = 0; + for (int i = 0; i < 5; ++i) + { + GameObject treeGo = Instantiate(treePrefab, new Vector3(x++, 0, 0), Quaternion.identity); + Tree tree = treeGo.GetComponent(); + tree.numLeaves = Random.Range(10, 200); + Debug.Log("Spawning leaf with leaf count " + tree.numLeaves); + ServerObjectManager.Spawn(treeGo); + } + } + // CodeEmbed-End: spawn-trees-example + + // CodeEmbed-Start: spawn-trees-authority-example + void SpawnTrees(INetworkPlayer player) + { + int x = 0; + for (int i = 0; i < 5; ++i) + { + GameObject treeGo = Instantiate(treePrefab, new Vector3(x++, 0, 0), Quaternion.identity); + Tree tree = treeGo.GetComponent(); + tree.numLeaves = Random.Range(10, 200); + Debug.Log("Spawning leaf with leaf count " + tree.numLeaves); + ServerObjectManager.Spawn(treeGo, player); + } + } + // CodeEmbed-End: spawn-trees-authority-example + } +} diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/SpawningExample.cs.meta b/Assets/Mirage/Samples~/Snippets/GameObjects/SpawningExample.cs.meta new file mode 100644 index 00000000000..8ffb8c5670b --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/SpawningExample.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c43613bd281f4f14fb14be86987670c8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/Tree.cs b/Assets/Mirage/Samples~/Snippets/GameObjects/Tree.cs new file mode 100644 index 00000000000..1299645b925 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/Tree.cs @@ -0,0 +1,55 @@ +using UnityEngine; +using Mirage; + +namespace Mirage.Snippets.GameObjects +{ + // CodeEmbed-Start: tree-syncvar-example + public class Tree : NetworkBehaviour + { + [SyncVar] + public int numLeaves { get; set; } + + void Start() + { + Identity.OnStartClient.AddListener(OnStartClient); + } + + public void OnStartClient() + { + Debug.Log("Tree spawned with leaf count " + numLeaves); + } + } + // CodeEmbed-End: tree-syncvar-example + + public class TreeAuthorityExample : NetworkBehaviour + { + public GameObject treePrefab; + public ClientObjectManager ClientObjectManager; + public NetworkClient NetworkClient; + public int numLeaves; + + private void OnClientConnect(NetworkConnection conn, ConnectMessage msg) {} + + // CodeEmbed-Start: tree-client-authority + public void ClientConnect() + { + ClientObjectManager.spawnPrefabs.Add(treePrefab); + NetworkClient.Connect("localhost"); + NetworkClient.MessageHandler.RegisterHandler(OnClientConnect); + + NetworkClient.Player.Identity.OnAuthorityChanged.AddListener(OnStartAuthority); + } + + public void OnStartAuthority(bool changed) + { + CmdMessageFromTree("Tree with " + numLeaves + " reporting in"); + } + + [ServerRpc] + void CmdMessageFromTree(string msg) + { + Debug.Log("Client sent a tree message: " + msg); + } + // CodeEmbed-End: tree-client-authority + } +} diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/Tree.cs.meta b/Assets/Mirage/Samples~/Snippets/GameObjects/Tree.cs.meta new file mode 100644 index 00000000000..e534c314e88 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/Tree.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6d0d9e34d0d7fab47a7c1aadfa0614a6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/General.meta b/Assets/Mirage/Samples~/Snippets/General.meta new file mode 100644 index 00000000000..a106c919fdd --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/General.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0649700c662d74f428ce70837b31e280 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/General/AttributesSnippets.cs b/Assets/Mirage/Samples~/Snippets/General/AttributesSnippets.cs new file mode 100644 index 00000000000..f822304506e --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/General/AttributesSnippets.cs @@ -0,0 +1,25 @@ +using UnityEngine; +using Mirage; + +namespace Mirage.Snippets.General +{ + public class AttributesSnippets : NetworkBehaviour + { + // CodeEmbed-Start: attributes-server + [Server] + private void SpawnCoin() + { + // This method is only allowed to be invoked on the server. + } + // CodeEmbed-End: attributes-server + + // CodeEmbed-Start: attributes-network-method + [NetworkMethod(NetworkFlags.Server | NetworkFlags.NotActive)] + public void StartGame() + { + // This method will run on the server or in single-player mode. + // It will only be blocked if the client is active. + } + // CodeEmbed-End: attributes-network-method + } +} diff --git a/Assets/Mirage/Samples~/Snippets/General/AttributesSnippets.cs.meta b/Assets/Mirage/Samples~/Snippets/General/AttributesSnippets.cs.meta new file mode 100644 index 00000000000..93da26b6417 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/General/AttributesSnippets.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 10259b0457569a140b781d25fbfe1834 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/General/AuthoritySnippets.cs b/Assets/Mirage/Samples~/Snippets/General/AuthoritySnippets.cs new file mode 100644 index 00000000000..00c9aef48e7 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/General/AuthoritySnippets.cs @@ -0,0 +1,43 @@ +using UnityEngine; + +namespace Mirage.Snippets.General +{ + public class AuthoritySnippets : NetworkBehaviour + { + public GameObject prefab; + public ServerObjectManager ServerObjectManager; + + public void SpawnWithAuthority(INetworkPlayer owner) + { + // CodeEmbed-Start: authority-spawn-with-owner + GameObject go = Instantiate(prefab); + ServerObjectManager.Spawn(go, owner); + // CodeEmbed-End: authority-spawn-with-owner + } + + public void AssignAuthorityExample(INetworkPlayer conn) + { + // CodeEmbed-Start: authority-assign-client-authority + Identity.AssignClientAuthority(conn); + // CodeEmbed-End: authority-assign-client-authority + } + + public INetworkPlayer connectionToClient => Owner; + + // CodeEmbed-Start: authority-pickup-item + // Command on character object + [ServerRpc] + private void PickupItem(NetworkIdentity item) + { + item.AssignClientAuthority(connectionToClient); + } + // CodeEmbed-End: authority-pickup-item + + public void RemoveAuthorityExample() + { + // CodeEmbed-Start: authority-remove-client-authority + Identity.RemoveClientAuthority(); + // CodeEmbed-End: authority-remove-client-authority + } + } +} diff --git a/Assets/Mirage/Samples~/Snippets/General/AuthoritySnippets.cs.meta b/Assets/Mirage/Samples~/Snippets/General/AuthoritySnippets.cs.meta new file mode 100644 index 00000000000..618fa072437 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/General/AuthoritySnippets.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2bc69b9a54d699648a9984d1b6f5c900 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/General/BestPracticesSnippets.cs b/Assets/Mirage/Samples~/Snippets/General/BestPracticesSnippets.cs new file mode 100644 index 00000000000..f019c9843a3 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/General/BestPracticesSnippets.cs @@ -0,0 +1,13 @@ +using System; +using UnityEngine; + +namespace Mirage.Snippets.General +{ + // CodeEmbed-Start: best-practices-custom-message + public struct CreateVisualEffect + { + public Vector3 position; + public Guid prefabId; + } + // CodeEmbed-End: best-practices-custom-message +} diff --git a/Assets/Mirage/Samples~/Snippets/General/BestPracticesSnippets.cs.meta b/Assets/Mirage/Samples~/Snippets/General/BestPracticesSnippets.cs.meta new file mode 100644 index 00000000000..7e891379918 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/General/BestPracticesSnippets.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: afed630608e40524fbe859a1f4d7c8b4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/General/ClockSyncSnippets.cs b/Assets/Mirage/Samples~/Snippets/General/ClockSyncSnippets.cs new file mode 100644 index 00000000000..c71b9837005 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/General/ClockSyncSnippets.cs @@ -0,0 +1,42 @@ +using Mirage; + +namespace Mirage.Snippets.General +{ + public class ClockSyncSnippets + { + public void ShowTime(NetworkTime NetworkTime) + { + // CodeEmbed-Start: clock-sync-time + double now = NetworkTime.Time; + // CodeEmbed-End: clock-sync-time + } + + public void ShowRtt(NetworkTime NetworkTime) + { + // CodeEmbed-Start: clock-sync-rtt + double rtt = NetworkTime.Rtt; + // CodeEmbed-End: clock-sync-rtt + } + + public void ShowTimeSd(NetworkTime NetworkTime) + { + // CodeEmbed-Start: clock-sync-time-sd + double timeStandardDeviation = NetworkTime.TimeSd; + // CodeEmbed-End: clock-sync-time-sd + } + + public void ConfigurePing(NetworkTime NetworkTime) + { + // CodeEmbed-Start: clock-sync-ping-interval + NetworkTime.PingInterval = 2.0f; + // CodeEmbed-End: clock-sync-ping-interval + } + + public void ConfigurePingWindow(NetworkTime NetworkTime) + { + // CodeEmbed-Start: clock-sync-ping-window-size + NetworkTime.PingWindowSize = 10; + // CodeEmbed-End: clock-sync-ping-window-size + } + } +} diff --git a/Assets/Mirage/Samples~/Snippets/General/ClockSyncSnippets.cs.meta b/Assets/Mirage/Samples~/Snippets/General/ClockSyncSnippets.cs.meta new file mode 100644 index 00000000000..150a9315de5 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/General/ClockSyncSnippets.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fc0fc4773c03f604c995be1f364e054c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/General/ErrorHandlingSnippets.cs b/Assets/Mirage/Samples~/Snippets/General/ErrorHandlingSnippets.cs new file mode 100644 index 00000000000..89c2f99d93b --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/General/ErrorHandlingSnippets.cs @@ -0,0 +1,187 @@ +using System; +using UnityEngine; + +namespace Mirage.Snippets.General +{ + // CodeEmbed-Start: error-handling-flags + // see PlayerErrorFlags in the source code for most up-to-date values + [Flags] + public enum PlayerErrorFlags + { + None = 0, + + // Likely developer bugs + RpcNullException = 1 << 0, + RpcException = 1 << 1, + + // Connection/versioning issues + DeserializationException = 1 << 2, + RpcSync = 1 << 3, + RateLimit = 1 << 4, + + // Security/Malicious Intent + Unauthorized = 1 << 5, + Critical = 1 << 6, + LikelyCheater = 1 << 7, + + // Custom developer defined errors + CustomError = 1 << 16 + } + // CodeEmbed-End: error-handling-flags + + // CodeEmbed-Start: error-handling-custom-flags + public static class MyErrorFlags + { + public static readonly PlayerErrorFlags InvalidTrade = Custom(0); + public static readonly PlayerErrorFlags AnotherCustom = Custom(1); + + private static PlayerErrorFlags Custom(int index) + { + return (PlayerErrorFlags)(((int)PlayerErrorFlags.CustomError) << index); + } + } + // CodeEmbed-End: error-handling-custom-flags +} + +namespace Mirage.Snippets.General.CustomError +{ + // CodeEmbed-Start: error-handling-custom-error-class + public static class MyErrorFlags + { + public const PlayerErrorFlags InvalidAction = PlayerErrorFlags.CustomError; + } + // CodeEmbed-End: error-handling-custom-error-class + + public class CustomErrorBehaviour : NetworkBehaviour + { + private bool IsActionValid(int data) => true; + + // CodeEmbed-Start: error-handling-custom-error-method + [ServerRpc] + private void CmdDoSomething(int data) + { + // The IsActionValid method would contain your custom validation logic. + if (!IsActionValid(data)) + { + // Penalize the player with a moderate cost for sending invalid data. + Owner.SetError(10, MyErrorFlags.InvalidAction); + return; + } + + // ... process valid data + } + // CodeEmbed-End: error-handling-custom-error-method + } +} + +namespace Mirage.Snippets.General.AdminAction +{ + public class AdminActionBehaviour : NetworkBehaviour + { + private bool IsAdmin(INetworkPlayer player) => false; + + // CodeEmbed-Start: error-handling-admin-action + [ServerRpc] + private void CmdTryAdminAction(string command) + { + // The IsAdmin method would check if the player has admin privileges. + if (!IsAdmin(Owner)) + { + // A non-admin tried to use an admin command. + // Set cost higher than MaxTokens (default 200) to trigger the limit immediately. + Owner.SetError(10000, PlayerErrorFlags.Critical); + return; + } + + // ... execute admin command + } + // CodeEmbed-End: error-handling-admin-action + } +} + +namespace Mirage.Snippets.General.PublicMessage +{ + public class PublicMessageBehaviour : NetworkBehaviour + { + private bool CheckMessageRateLimit(INetworkPlayer sender) => false; + + // CodeEmbed-Start: error-handling-public-message + [Client] + public void SendPublicMessage(string message) + { + // client side check before sending message + if (string.IsNullOrWhiteSpace(message) || message.Length > 100) + return; + + CmdSendPublicMessage(message); + } + + [ServerRpc(requireAuthority = false)] + private void CmdSendPublicMessage(string message, INetworkPlayer sender = null) + { + if (string.IsNullOrWhiteSpace(message) || message.Length > 100) + { + // Invalid message length. this is very likely a cheat because message length is checked on client before + // how ever this is just chat message nothing not critical gameplay + // for example could be from chat mod with higher size that they left on after playing on a modded server + sender.SetError(50, PlayerErrorFlags.LikelyCheater); + return; + } + + if (CheckMessageRateLimit(sender)) + { + // player sent more message than chat rate limit, just use low cost + sender.SetError(1, PlayerErrorFlags.None); + return; + } + + // ... + } + // CodeEmbed-End: error-handling-public-message + } +} + +namespace Mirage.Snippets.General.CustomErrorHandler +{ + public static class MyErrorFlags + { + public const PlayerErrorFlags InvalidAction = PlayerErrorFlags.CustomError << 0; + } + + // CodeEmbed-Start: error-handling-custom-handler + public class MyGameServer : MonoBehaviour + { + public NetworkServer server; + + private void Start() + { + server.SetErrorRateLimitReachedCallback(OnPlayerErrorLimitReached); + } + + private void OnPlayerErrorLimitReached(INetworkPlayer player) + { + Debug.LogWarning($"Player {player} reached error limit with flags: {player.ErrorFlags}"); + + if ((player.ErrorFlags & PlayerErrorFlags.Critical) != 0) + { + // For critical errors, always disconnect. + player.Disconnect(); + + // ... add player to ban or timeout list here so they can't reconnect + + return; + } + else if ((player.ErrorFlags & MyErrorFlags.InvalidAction) != 0) + { + // For our custom action, maybe just send a warning. + // Note: You would need to implement the ChatMessage struct and its handler. + // player.Send(new ChatMessage("You are performing too many invalid actions.")); + } + // ... other custom logic + + // Reset flags after handling + player.ResetErrorFlag(); + } + } + // CodeEmbed-End: error-handling-custom-handler +} diff --git a/Assets/Mirage/Samples~/Snippets/General/ErrorHandlingSnippets.cs.meta b/Assets/Mirage/Samples~/Snippets/General/ErrorHandlingSnippets.cs.meta new file mode 100644 index 00000000000..181abcaf2c3 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/General/ErrorHandlingSnippets.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 279b7641ff728224ab744d25ebf5c263 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/General/GettingStartedSnippets.cs b/Assets/Mirage/Samples~/Snippets/General/GettingStartedSnippets.cs new file mode 100644 index 00000000000..ebf50ab46d5 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/General/GettingStartedSnippets.cs @@ -0,0 +1,40 @@ +using UnityEngine; + +namespace Mirage.Snippets.General +{ + // CodeEmbed-Start: getting-started-client-authority + public class GettingStartedClientAuthority : NetworkBehaviour + { + private void Update() + { + if (!IsLocalPlayer) + return; + + // handle player input for movement + } + } + // CodeEmbed-End: getting-started-client-authority + + // CodeEmbed-Start: getting-started-server-authority + public class GettingStartedServerAuthority : NetworkBehaviour + { + private void Update() + { + if (!IsLocalPlayer) + return; + + // handle player input for movement + + // You would call this command after handling input or you can send inputs directly to + // server and let server buffer inputs up and do movements based on the buffered inputs. + MovePlayer(); + } + + [ServerRpc] + private void MovePlayer() + { + // We are now firing off some kind of movement all done by server. + } + } + // CodeEmbed-End: getting-started-server-authority +} diff --git a/Assets/Mirage/Samples~/Snippets/General/GettingStartedSnippets.cs.meta b/Assets/Mirage/Samples~/Snippets/General/GettingStartedSnippets.cs.meta new file mode 100644 index 00000000000..fd7bb650f0a --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/General/GettingStartedSnippets.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 428dd0a5ce8b30d49929c39c5c2da0d2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/General/TroubleshootingSnippets.cs b/Assets/Mirage/Samples~/Snippets/General/TroubleshootingSnippets.cs new file mode 100644 index 00000000000..32c70d38dac --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/General/TroubleshootingSnippets.cs @@ -0,0 +1,30 @@ +using Mirage; +using Mirage.Collections; + +namespace Mirage.Snippets.General +{ + // CodeEmbed-Start: troubleshooting-no-writer-triggering + public struct MyCustomType + { + public int id; + public string name; + } + + public class MyBehaviour : NetworkBehaviour + { + private readonly SyncList myList = new SyncList(); + } + // CodeEmbed-End: troubleshooting-no-writer-triggering +} + +namespace Mirage.Snippets.General.Resolved +{ + // CodeEmbed-Start: troubleshooting-no-writer-resolved + [NetworkMessage] + public struct MyCustomType + { + public int id; + public string name; + } + // CodeEmbed-End: troubleshooting-no-writer-resolved +} diff --git a/Assets/Mirage/Samples~/Snippets/General/TroubleshootingSnippets.cs.meta b/Assets/Mirage/Samples~/Snippets/General/TroubleshootingSnippets.cs.meta new file mode 100644 index 00000000000..171ccdd357b --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/General/TroubleshootingSnippets.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6ab7c5411b613af40ad1c080be64ed97 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Guides.meta b/Assets/Mirage/Samples~/Snippets/Guides.meta new file mode 100644 index 00000000000..cb7ad8d2be4 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Guides.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ec88532eca944eb4db1efe5f3e4bf87c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Guides/FaqSnippets.cs b/Assets/Mirage/Samples~/Snippets/Guides/FaqSnippets.cs new file mode 100644 index 00000000000..1958302ffad --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Guides/FaqSnippets.cs @@ -0,0 +1,22 @@ +using Mirage; +using UnityEngine; + +namespace Mirage.Snippets.Guides +{ + public class FaqSnippets : NetworkBehaviour + { + // CodeEmbed-Start: faq-custom-data + [ClientRpc] + public void RpcDoSomething(MyCustomStruct data) + { + // do stuff here + } + + struct MyCustomStruct + { + int someNumber; + Vector3 somePosition; + } + // CodeEmbed-End: faq-custom-data + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Guides/FaqSnippets.cs.meta b/Assets/Mirage/Samples~/Snippets/Guides/FaqSnippets.cs.meta new file mode 100644 index 00000000000..1e0150579dd --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Guides/FaqSnippets.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d5d60d29e2702794ea94145c9e74fde3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Messaging.meta b/Assets/Mirage/Samples~/Snippets/Messaging.meta new file mode 100644 index 00000000000..241f71d1e60 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Messaging.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1ee9ddfc199ae1b49a7013c17a166539 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/SendNetworkMessage.cs b/Assets/Mirage/Samples~/Snippets/Messaging/SendNetworkMessage.cs similarity index 96% rename from Assets/Mirage/Samples~/Snippets/SendNetworkMessage.cs rename to Assets/Mirage/Samples~/Snippets/Messaging/SendNetworkMessage.cs index 5419f18d29c..06387dabaff 100644 --- a/Assets/Mirage/Samples~/Snippets/SendNetworkMessage.cs +++ b/Assets/Mirage/Samples~/Snippets/Messaging/SendNetworkMessage.cs @@ -1,6 +1,6 @@ using UnityEngine; -namespace Mirage.Snippets.SendNetworkMessages +namespace Mirage.Snippets.Messaging { // CodeEmbed-Start: send-score diff --git a/Assets/Mirage/Samples~/Snippets/SendNetworkMessage.cs.meta b/Assets/Mirage/Samples~/Snippets/Messaging/SendNetworkMessage.cs.meta similarity index 83% rename from Assets/Mirage/Samples~/Snippets/SendNetworkMessage.cs.meta rename to Assets/Mirage/Samples~/Snippets/Messaging/SendNetworkMessage.cs.meta index cf14eccb473..a9c2a57a1a0 100644 --- a/Assets/Mirage/Samples~/Snippets/SendNetworkMessage.cs.meta +++ b/Assets/Mirage/Samples~/Snippets/Messaging/SendNetworkMessage.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 96645a0709525804c86b40f7f2af108b +guid: 228a9a29abee9954c802f4f956bebfa1 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/Mirage/Samples~/Snippets/RemoteActions.meta b/Assets/Mirage/Samples~/Snippets/RemoteActions.meta new file mode 100644 index 00000000000..cce3d4ca7c2 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/RemoteActions.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0d912c74bbbc2ab4ba53016518298f9b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/RemoteActions/ClientRpcExamples.cs b/Assets/Mirage/Samples~/Snippets/RemoteActions/ClientRpcExamples.cs new file mode 100644 index 00000000000..1f8005795e8 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/RemoteActions/ClientRpcExamples.cs @@ -0,0 +1,95 @@ +using UnityEngine; +using Mirage; + +namespace Mirage.Snippets.RemoteActions.ClientRpcSimple +{ + // CodeEmbed-Start: client-rpc-attribute + public class MyClientRpcExampleBehaviour : NetworkBehaviour + { + [ClientRpc] + public void MyRpcFunction() + { + // Code to invoke on client + } + } + // CodeEmbed-End: client-rpc-attribute +} + +namespace Mirage.Snippets.RemoteActions.ClientRpcPlayer +{ + // CodeEmbed-Start: client-rpc-player + public class MyClientRpcExampleBehaviour : NetworkBehaviour + { + [ClientRpc(target = RpcTarget.Player)] + public void MyRpcFunction(NetworkPlayer target) + { + // Code to invoke on client + } + } + // CodeEmbed-End: client-rpc-player +} + +namespace Mirage.Snippets.RemoteActions.ClientRpcHealth +{ + // CodeEmbed-Start: client-rpc-example-health + public class Player : NetworkBehaviour + { + private int health; + + public void TakeDamage(int amount) + { + if (!IsServer) + return; + + health -= amount; + Damage(amount); + } + + [ClientRpc] + private void Damage(int amount) + { + Debug.Log("Took damage:" + amount); + } + } + // CodeEmbed-End: client-rpc-example-health +} + +namespace Mirage.Snippets.RemoteActions.ClientRpcMagic +{ + // CodeEmbed-Start: client-rpc-example-magic + public class Player : NetworkBehaviour + { + private int health; + + [Server] + private void Magic(GameObject target, int damage) + { + target.GetComponent().health -= damage; + + NetworkIdentity opponentIdentity = target.GetComponent(); + DoMagic(opponentIdentity.Owner, damage); + } + + [ClientRpc(target = RpcTarget.Player)] + public void DoMagic(INetworkPlayer target, int damage) + { + // This will appear on the opponent's client, not the attacking player's + Debug.Log($"Magic Damage = {damage}"); + } + + [Server] + private void HealMe() + { + health += 10; + Healed(10); + } + + [ClientRpc(target = RpcTarget.Owner)] + public void Healed(int amount) + { + // No NetworkPlayer parameter, so it goes to owner + Debug.Log($"Health increased by {amount}"); + } + } + // CodeEmbed-End: client-rpc-example-magic +} diff --git a/Assets/Mirage/Samples~/Snippets/RemoteActions/ClientRpcExamples.cs.meta b/Assets/Mirage/Samples~/Snippets/RemoteActions/ClientRpcExamples.cs.meta new file mode 100644 index 00000000000..9a7ba8ec919 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/RemoteActions/ClientRpcExamples.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cc62286e931197c4e803711d47e7fc2e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/RemoteActions/RateLimitingExamples.cs b/Assets/Mirage/Samples~/Snippets/RemoteActions/RateLimitingExamples.cs new file mode 100644 index 00000000000..36fb1dceedc --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/RemoteActions/RateLimitingExamples.cs @@ -0,0 +1,36 @@ +using UnityEngine; +using Mirage; + +namespace Mirage.Snippets.RemoteActions +{ + // CodeEmbed-Start: rate-limiting-emote + public class PlayerSocial : NetworkBehaviour + { + // Allows 1 emote every 2 seconds, but the player can burst up to 3 emotes consecutively. + // Spamming emotes too fast will drop the call and apply a penalty to their error limit. + // Penalty set to 0 to not penalize the player for sending too many emotes, we can just ignore them. + [ServerRpc] + [RateLimit(Interval = 2f, Refill = 1, MaxTokens = 3, Penalty = 0)] + public void CmdSendEmote(int emoteId) + { + // ... process and broadcast emote to other players ... + } + } + // CodeEmbed-End: rate-limiting-emote + + // CodeEmbed-Start: rate-limiting-move + public class PlayerRTSController : NetworkBehaviour + { + // Players should realistically only be issuing a few move commands a second. + // We allow a healthy burst of 10 commands if they are furiously clicking across the map. + // However, if a cheat sends 100 move instructions instantly, the large Penalty (10) + // will quickly exceed the global error threshold and kick them. + [ServerRpc] + [RateLimit(Interval = 1f, Refill = 5, MaxTokens = 10, Penalty = 10)] + public void CmdMoveUnit(NetworkIdentity unit, Vector3 targetPosition) + { + // ... process movement command ... + } + } + // CodeEmbed-End: rate-limiting-move +} diff --git a/Assets/Mirage/Samples~/Snippets/RemoteActions/RateLimitingExamples.cs.meta b/Assets/Mirage/Samples~/Snippets/RemoteActions/RateLimitingExamples.cs.meta new file mode 100644 index 00000000000..ae3b011d9c7 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/RemoteActions/RateLimitingExamples.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e8f66a7c2a1614541a8e5098b8b1cd57 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/RemoteActions/RpcExampleChangeName.cs b/Assets/Mirage/Samples~/Snippets/RemoteActions/RpcExampleChangeName.cs new file mode 100644 index 00000000000..e2085d9aebd --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/RemoteActions/RpcExampleChangeName.cs @@ -0,0 +1,18 @@ +using Mirage; + +namespace Mirage.Snippets.RemoteActions.RpcExampleChangeName +{ + // CodeEmbed-Start: rpc-example-change-name + public class Player : NetworkBehaviour + { + [SyncVar] + public string PlayerName { get; set; } + + [ServerRpc] + public void RpcChangeName(string newName) + { + PlayerName = newName; + } + } + // CodeEmbed-End: rpc-example-change-name +} diff --git a/Assets/Mirage/Samples~/Snippets/RemoteActions/RpcExampleChangeName.cs.meta b/Assets/Mirage/Samples~/Snippets/RemoteActions/RpcExampleChangeName.cs.meta new file mode 100644 index 00000000000..a2efc15cd80 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/RemoteActions/RpcExampleChangeName.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ad35800cdc591e44883fe3ca3547f035 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/RemoteActions/RpcExampleChangeNameGenerated.cs b/Assets/Mirage/Samples~/Snippets/RemoteActions/RpcExampleChangeNameGenerated.cs new file mode 100644 index 00000000000..e0983d25faa --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/RemoteActions/RpcExampleChangeNameGenerated.cs @@ -0,0 +1,71 @@ +using System; +using Mirage; +using Mirage.Serialization; +using Mirage.RemoteCalls; +using NetworkBehaviour = Mirage.Snippets.RemoteActions.RpcExamplesGenerated.DummyNetworkBehaviour; + +namespace Mirage.Snippets.RemoteActions.RpcExamplesGenerated +{ + // Dummy classes/aliases to make the snippet code compile in Unity + public class DummyNetworkBehaviour + { + public bool IsServer { get; set; } + public DummyRemoteCallCollection remoteCallCollection { get; set; } = new DummyRemoteCallCollection(); + protected virtual int GetRpcCount() => 0; + } + + public class DummyRemoteCallCollection + { + public void Register(int id, Type type, string name, RpcInvokeType invokeType, CmdDelegate cmdDelegate, bool requireAuthority) {} + } + + public delegate void CmdDelegate(NetworkReader reader, INetworkPlayer senderConnection, int replyId); + + public static class ServerRpcSender + { + public static void Send(DummyNetworkBehaviour behaviour, int index, PooledNetworkWriter writer, int channel, bool requireAuthority) {} + } + + // CodeEmbed-Start: rpc-example-change-name-generated + public class Player : NetworkBehaviour + { + [SyncVar] + public string PlayerName { get; set; } + + [ServerRpc] + public void RpcChangeName(string newName) + { + if (this.IsServer) + UserCode_RpcChangeName_123456789(newName); + else + { + using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + { + writer.WriteString(newName); + ServerRpcSender.Send(this, 123456789, writer, 0, true); + } + } + } + + public void UserCode_RpcChangeName_123456789(string newName) + { + PlayerName = newName; + } + + protected void Skeleton_RpcChangeName_123456789(NetworkReader reader, INetworkPlayer senderConnection, int replyId) + { + this.UserCode_RpcChangeName_123456789(reader.ReadString()); + } + + public Player() + { + this.remoteCallCollection.Register(0, typeof(Player), "Player.RpcChangeName", RpcInvokeType.ServerRpc, new CmdDelegate(Skeleton_RpcChangeName_123456789), true); + } + + protected override int GetRpcCount() + { + return 1; + } + } + // CodeEmbed-End: rpc-example-change-name-generated +} diff --git a/Assets/Mirage/Samples~/Snippets/RemoteActions/RpcExampleChangeNameGenerated.cs.meta b/Assets/Mirage/Samples~/Snippets/RemoteActions/RpcExampleChangeNameGenerated.cs.meta new file mode 100644 index 00000000000..134b9e8ab16 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/RemoteActions/RpcExampleChangeNameGenerated.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b480294cf6676b544adee44081010ab4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/RemoteActions/ServerRpcDoor.cs b/Assets/Mirage/Samples~/Snippets/RemoteActions/ServerRpcDoor.cs new file mode 100644 index 00000000000..aaa52dfcdc5 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/RemoteActions/ServerRpcDoor.cs @@ -0,0 +1,36 @@ +using UnityEngine; +using Mirage; +using INetworkPlayer = Mirage.Snippets.RemoteActions.ServerRpcDoor.IDummyNetworkPlayer; + +namespace Mirage.Snippets.RemoteActions.ServerRpcDoor +{ + public interface IDummyNetworkPlayer : INetworkPlayer + { + NetworkIdentity identity { get; } + } + + public class Player : MonoBehaviour + { + public bool hasDoorKey; + } + + // CodeEmbed-Start: server-rpc-door + public enum DoorState : byte + { + Open, Closed + } + + public class Door : NetworkBehaviour + { + [SyncVar] + public DoorState doorState { get; set; } + + [ServerRpc(requireAuthority = false)] + public void CmdSetDoorState(DoorState newDoorState, INetworkPlayer sender = null) + { + if (sender.identity.GetComponent().hasDoorKey) + doorState = newDoorState; + } + } + // CodeEmbed-End: server-rpc-door +} diff --git a/Assets/Mirage/Samples~/Snippets/RemoteActions/ServerRpcDoor.cs.meta b/Assets/Mirage/Samples~/Snippets/RemoteActions/ServerRpcDoor.cs.meta new file mode 100644 index 00000000000..e92de205c46 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/RemoteActions/ServerRpcDoor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c0b0fb7d63e245a42a59bf2d8de3dbe4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/RemoteActions/ServerRpcDropCube.cs b/Assets/Mirage/Samples~/Snippets/RemoteActions/ServerRpcDropCube.cs new file mode 100644 index 00000000000..eeeb29e4b41 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/RemoteActions/ServerRpcDropCube.cs @@ -0,0 +1,40 @@ +using UnityEngine; +using Mirage; + +namespace Mirage.Snippets.RemoteActions.ServerRpcDropCube +{ + // Dummy class to make the snippet code compile + public static class NetworkServer + { + public static void Spawn(GameObject obj) {} + } + + // CodeEmbed-Start: server-rpc-drop-cube + public class Player : NetworkBehaviour + { + // Assigned in inspector + public GameObject cubePrefab; + + private void Update() + { + if (!IsLocalPlayer) + return; + + if (Input.GetKey(KeyCode.X)) + DropCube(); + } + + [ServerRpc] + private void DropCube() + { + if (cubePrefab != null) + { + Vector3 spawnPos = transform.position + transform.forward * 2; + Quaternion spawnRot = transform.rotation; + GameObject cube = Instantiate(cubePrefab, spawnPos, spawnRot); + NetworkServer.Spawn(cube); + } + } + } + // CodeEmbed-End: server-rpc-drop-cube +} diff --git a/Assets/Mirage/Samples~/Snippets/RemoteActions/ServerRpcDropCube.cs.meta b/Assets/Mirage/Samples~/Snippets/RemoteActions/ServerRpcDropCube.cs.meta new file mode 100644 index 00000000000..4ce1dafeda7 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/RemoteActions/ServerRpcDropCube.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3850e52a68ad9ee448c76ae998d594f9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Rpc.meta b/Assets/Mirage/Samples~/Snippets/Rpc.meta new file mode 100644 index 00000000000..01c5e4f552f --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Rpc.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6b3c17d0ed0566943a9705e29e635843 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/RpcReply.cs b/Assets/Mirage/Samples~/Snippets/Rpc/RpcReply.cs similarity index 97% rename from Assets/Mirage/Samples~/Snippets/RpcReply.cs rename to Assets/Mirage/Samples~/Snippets/Rpc/RpcReply.cs index f721892208f..db45d129ba3 100644 --- a/Assets/Mirage/Samples~/Snippets/RpcReply.cs +++ b/Assets/Mirage/Samples~/Snippets/Rpc/RpcReply.cs @@ -1,7 +1,7 @@ using Cysharp.Threading.Tasks; using UnityEngine; -namespace Mirage.Snippets.ClientRpcReply +namespace Mirage.Snippets.Rpc { // CodeEmbed-Start: client-rpc-reply public class SelectCharacter : NetworkBehaviour diff --git a/Assets/Mirage/Samples~/Snippets/RpcReply.cs.meta b/Assets/Mirage/Samples~/Snippets/Rpc/RpcReply.cs.meta similarity index 83% rename from Assets/Mirage/Samples~/Snippets/RpcReply.cs.meta rename to Assets/Mirage/Samples~/Snippets/Rpc/RpcReply.cs.meta index fc96d268cd6..3fbc7015cc2 100644 --- a/Assets/Mirage/Samples~/Snippets/RpcReply.cs.meta +++ b/Assets/Mirage/Samples~/Snippets/Rpc/RpcReply.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 500dc9df0f6105748befd3a67a32e5ab +guid: 2b3c08bc095b29f4788df532c15ece61 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/Mirage/Samples~/Snippets/SceneLoading.meta b/Assets/Mirage/Samples~/Snippets/SceneLoading.meta new file mode 100644 index 00000000000..92c4334e183 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/SceneLoading.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 27ecbc69677a96e44aaa41f1aadc7ac6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/SceneLoading/CustomSceneManager.cs b/Assets/Mirage/Samples~/Snippets/SceneLoading/CustomSceneManager.cs new file mode 100644 index 00000000000..8004ee81a0f --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/SceneLoading/CustomSceneManager.cs @@ -0,0 +1,57 @@ +using UnityEngine; +using Mirage; + +namespace Mirage.Snippets.SceneLoading.OverrideOnServerAuthenticated +{ +#pragma warning disable CS0618 // Type or member is obsolete + // CodeEmbed-Start: custom-scene-manager-on-server-authenticated + public class MySceneManager : NetworkSceneManager + { + protected internal override void OnServerAuthenticated(INetworkPlayer player) + { + // just load server's active scene instead of all additive scenes as well + player.Send(new SceneMessage { MainActivateScene = ActiveScenePath }); + player.Send(new SceneReadyMessage()); + } + } + // CodeEmbed-End: custom-scene-manager-on-server-authenticated +#pragma warning restore CS0618 +} + +namespace Mirage.Snippets.SceneLoading.OverrideStart +{ +#pragma warning disable CS0618 // Type or member is obsolete + // CodeEmbed-Start: custom-scene-manager-start + public class MySceneManager : NetworkSceneManager + { + protected internal override void Start() + { + // add your stuff before. + + base.Start(); + + // add your stuff after. + } + } + // CodeEmbed-End: custom-scene-manager-start +#pragma warning restore CS0618 +} + +namespace Mirage.Snippets.SceneLoading.OverrideOnDestroy +{ +#pragma warning disable CS0618 // Type or member is obsolete + // CodeEmbed-Start: custom-scene-manager-on-destroy + public class MySceneManager : NetworkSceneManager + { + protected internal override void OnDestroy() + { + // add your stuff before. + + base.OnDestroy(); + + // add your stuff after. + } + } + // CodeEmbed-End: custom-scene-manager-on-destroy +#pragma warning restore CS0618 +} diff --git a/Assets/Mirage/Samples~/Snippets/SceneLoading/CustomSceneManager.cs.meta b/Assets/Mirage/Samples~/Snippets/SceneLoading/CustomSceneManager.cs.meta new file mode 100644 index 00000000000..f24cca3aff2 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/SceneLoading/CustomSceneManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3ed53b7b558a0054d87538bf0bbfdaa4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/SceneLoading/FixedMatchSize.cs b/Assets/Mirage/Samples~/Snippets/SceneLoading/FixedMatchSize.cs new file mode 100644 index 00000000000..c4935992487 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/SceneLoading/FixedMatchSize.cs @@ -0,0 +1,38 @@ +using UnityEngine; +using Mirage; + +namespace Mirage.Snippets.SceneLoading +{ + public class CustomSceneLoader : MonoBehaviour + { + public NetworkServer Server; + + // CodeEmbed-Start: fixed-match-size + // Modify this inside your duplicated class + private void HandleSceneReadyMessage(INetworkPlayer player, SceneReadyMessage message) + { + player.SceneIsReady = true; + + // Check if everyone has finished loading + bool allReady = true; + foreach (var p in Server.AllPlayers) + { + if (!p.SceneIsReady) + { + allReady = false; + break; + } + } + + // Only spawn characters once everyone is fully loaded + if (allReady) + foreach (var p in Server.AllPlayers) + SpawnCharacterForPlayer(p); + } + // CodeEmbed-End: fixed-match-size + + private void SpawnCharacterForPlayer(INetworkPlayer player) + { + } + } +} diff --git a/Assets/Mirage/Samples~/Snippets/SceneLoading/FixedMatchSize.cs.meta b/Assets/Mirage/Samples~/Snippets/SceneLoading/FixedMatchSize.cs.meta new file mode 100644 index 00000000000..1f7f4cfea94 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/SceneLoading/FixedMatchSize.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dcced74595bb9ec479094b7cf1504a88 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneAdditively.cs b/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneAdditively.cs new file mode 100644 index 00000000000..49b00d52249 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneAdditively.cs @@ -0,0 +1,19 @@ +using UnityEngine; +using Mirage; + +namespace Mirage.Snippets.SceneLoading +{ + // CodeEmbed-Start: load-scene-additively + public class LoadSceneAdditively : MonoBehaviour + { + public void Start() + { + NetworkSceneManager sceneManager = GetComponent(); + +#pragma warning disable CS0618 // Type or member is obsolete + sceneManager.ServerLoadSceneAdditively("path to scene asset file.", sceneManager.Server.Players); +#pragma warning restore CS0618 // Type or member is obsolete + } + } + // CodeEmbed-End: load-scene-additively +} diff --git a/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneAdditively.cs.meta b/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneAdditively.cs.meta new file mode 100644 index 00000000000..935843dc727 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneAdditively.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fa189c36e84e77a46a646de39f04e39a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneAdditivelyPlayer.cs b/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneAdditivelyPlayer.cs new file mode 100644 index 00000000000..86b612fb34e --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneAdditivelyPlayer.cs @@ -0,0 +1,29 @@ +using UnityEngine; +using Mirage; + +namespace Mirage.Snippets.SceneLoading +{ + public class LoadSceneAdditivelyPlayer : MonoBehaviour + { + public void Example(NetworkSceneManager sceneManager, INetworkPlayer Player) + { + // CodeEmbed-Start: load-scene-additively-player + sceneManager.ServerLoadSceneAdditively("path to scene asset file.", Player); + // CodeEmbed-End: load-scene-additively-player + } + + public void ExampleNormal(NetworkSceneManager sceneManager, INetworkPlayer Player) + { + // CodeEmbed-Start: load-scene-additively-player-normal + sceneManager.ServerLoadSceneAdditively("path to scene asset file.", Player, true); + // CodeEmbed-End: load-scene-additively-player-normal + } + + public void ExamplePhysics(NetworkSceneManager sceneManager, INetworkPlayer Player) + { + // CodeEmbed-Start: load-scene-additively-player-physics + sceneManager.ServerLoadSceneAdditively("path to scene asset file.", Player, false, new UnityEngine.SceneManagement.LoadSceneParameters { loadSceneMode = UnityEngine.SceneManagement.LoadSceneMode.Additive, localPhysicsMode = UnityEngine.SceneManagement.LocalPhysicsMode.Physics2D }); + // CodeEmbed-End: load-scene-additively-player-physics + } + } +} diff --git a/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneAdditivelyPlayer.cs.meta b/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneAdditivelyPlayer.cs.meta new file mode 100644 index 00000000000..3e7313ff9db --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneAdditivelyPlayer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c40ffc248641cda4daeb06ef34feccd4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneNormal.cs b/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneNormal.cs new file mode 100644 index 00000000000..1e624f4a03b --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneNormal.cs @@ -0,0 +1,17 @@ +using UnityEngine; +using Mirage; + +namespace Mirage.Snippets.SceneLoading +{ + // CodeEmbed-Start: load-scene-normal + public class LoadScene : MonoBehaviour + { + public void Start() + { + NetworkSceneManager sceneManager = GetComponent(); + + sceneManager.ServerLoadSceneNormal("path to scene asset file."); + } + } + // CodeEmbed-End: load-scene-normal +} diff --git a/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneNormal.cs.meta b/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneNormal.cs.meta new file mode 100644 index 00000000000..24c95495f04 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneNormal.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 994f3236c483bf2418e8d5db51069492 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneNormalParams.cs b/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneNormalParams.cs new file mode 100644 index 00000000000..ef75dbea314 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneNormalParams.cs @@ -0,0 +1,16 @@ +using UnityEngine; +using UnityEngine.SceneManagement; +using Mirage; + +namespace Mirage.Snippets.SceneLoading +{ + public class LoadSceneNormalParams : MonoBehaviour + { + public void Example(NetworkSceneManager sceneManager) + { + // CodeEmbed-Start: load-scene-normal-params + sceneManager.ServerLoadSceneNormal("path to scene asset file.", new LoadSceneParameters { loadSceneMode = LoadSceneMode.Single, localPhysicsMode = LocalPhysicsMode.Physics2D }); + // CodeEmbed-End: load-scene-normal-params + } + } +} diff --git a/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneNormalParams.cs.meta b/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneNormalParams.cs.meta new file mode 100644 index 00000000000..04558fa61fe --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneNormalParams.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ba9159919a89da64284ab73499b0f2c1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/SceneLoading/LoaderUsage.cs b/Assets/Mirage/Samples~/Snippets/SceneLoading/LoaderUsage.cs new file mode 100644 index 00000000000..0b7fc123d1c --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/SceneLoading/LoaderUsage.cs @@ -0,0 +1,19 @@ +using UnityEngine; +using Mirage; +using Mirage.Components; +using Cysharp.Threading.Tasks; + +namespace Mirage.Snippets.SceneLoading +{ + // CodeEmbed-Start: loader-usage + public class MyGameManager : MonoBehaviour + { + public NetworkSceneLoader SceneLoader; + + public void StartGame() + { + SceneLoader.ServerLoadScene("BattleMap").Forget(); + } + } + // CodeEmbed-End: loader-usage +} diff --git a/Assets/Mirage/Samples~/Snippets/SceneLoading/LoaderUsage.cs.meta b/Assets/Mirage/Samples~/Snippets/SceneLoading/LoaderUsage.cs.meta new file mode 100644 index 00000000000..e9385925cb4 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/SceneLoading/LoaderUsage.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fd1bade92e2986d4fa22b3a63436b7bb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/SceneLoading/UnLoadSceneAdditively.cs b/Assets/Mirage/Samples~/Snippets/SceneLoading/UnLoadSceneAdditively.cs new file mode 100644 index 00000000000..e6fe424bae6 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/SceneLoading/UnLoadSceneAdditively.cs @@ -0,0 +1,21 @@ +using UnityEngine; +using UnityEngine.SceneManagement; +using Mirage; + +namespace Mirage.Snippets.SceneLoading +{ +#pragma warning disable CS0618 // Type or member is obsolete + // CodeEmbed-Start: unload-scene-additively + public class UnLoadSceneAdditively : MonoBehaviour + { + public void Start() + { + NetworkSceneManager sceneManager = GetComponent(); + + Scene scene = SceneManager.GetSceneByPath("path to scene asset file."); + sceneManager.ServerUnloadSceneAdditively(scene, sceneManager.Server.Players); + } + } + // CodeEmbed-End: unload-scene-additively +#pragma warning restore CS0618 +} diff --git a/Assets/Mirage/Samples~/Snippets/SceneLoading/UnLoadSceneAdditively.cs.meta b/Assets/Mirage/Samples~/Snippets/SceneLoading/UnLoadSceneAdditively.cs.meta new file mode 100644 index 00000000000..2c6e88720c7 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/SceneLoading/UnLoadSceneAdditively.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 105c4e2be05c0ef42a2aaeb6ca80d7f0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Serialization.meta b/Assets/Mirage/Samples~/Snippets/Serialization.meta new file mode 100644 index 00000000000..46e789933af --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Serialization.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ecc2f2d7830f2cb438fa209405692237 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Serialization/CustomReadWrite.cs b/Assets/Mirage/Samples~/Snippets/Serialization/CustomReadWrite.cs new file mode 100644 index 00000000000..7fe7308d27e --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Serialization/CustomReadWrite.cs @@ -0,0 +1,25 @@ +using Mirage.Serialization; + +namespace Mirage.Snippets.Serialization +{ + public struct MyType + { + public int value; + } + + public static class CustomReadWrite + { + // CodeEmbed-Start: custom-read-write + public static void WriteMyType(this NetworkWriter writer, MyType value) + { + // write MyType data here + } + + public static MyType ReadMyType(this NetworkReader reader) + { + // read MyType data here + return default; + } + // CodeEmbed-End: custom-read-write + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Serialization/CustomReadWrite.cs.meta b/Assets/Mirage/Samples~/Snippets/Serialization/CustomReadWrite.cs.meta new file mode 100644 index 00000000000..656b80c634c --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Serialization/CustomReadWrite.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 39001a4c59442234791a996488d6df92 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Serialization/DataTypesSnippets.cs b/Assets/Mirage/Samples~/Snippets/Serialization/DataTypesSnippets.cs new file mode 100644 index 00000000000..4c7a1bf8780 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Serialization/DataTypesSnippets.cs @@ -0,0 +1,183 @@ +using System; +using System.Collections; +using UnityEngine; +using Mirage.Serialization; + +namespace Mirage.Snippets.Serialization.GameObjectLookup +{ + public class GameObjectLookup : NetworkBehaviour + { + // CodeEmbed-Start: game-object-lookup + public GameObject target; + + [SyncVar(hook = nameof(OnTargetChanged))] + public uint targetID { get; set; } + + void OnTargetChanged(uint _, uint newValue) + { + if (NetworkIdentity.World.Spawned.TryGetValue(targetID, out NetworkIdentity identity)) + target = identity.gameObject; + else + StartCoroutine(SetTarget()); + } + + IEnumerator SetTarget() + { + while (target == null) + { + yield return null; + if (NetworkIdentity.World.SpawnedObjects.TryGetValue(targetID, out NetworkIdentity identity)) + target = identity.gameObject; + } + } + // CodeEmbed-End: game-object-lookup + } +} + +namespace Mirage.Snippets.Serialization.DateTimeSerializer +{ + // CodeEmbed-Start: datetime-serializer + public static class DateTimeReaderWriter + { + public static void WriteDateTime(this NetworkWriter writer, DateTime dateTime) + { + writer.WriteInt64(dateTime.Ticks); + } + + public static DateTime ReadDateTime(this NetworkReader reader) + { + return new DateTime(reader.ReadInt64()); + } + } + // CodeEmbed-End: datetime-serializer +} + +namespace Mirage.Snippets.Serialization.PolymorphicEquip +{ + // CodeEmbed-Start: polymorphic-equip + class Item + { + public string name; + } + + class Weapon : Item + { + public int hitPoints; + } + + class Armor : Item + { + public int hitPoints; + public int level; + } + + class Player : NetworkBehaviour + { + [ServerRpc] + void ServerRpcEquip(Item item) + { + // IMPORTANT: this does not work. Mirage will pass you an object of type item + // even if you pass a weapon or an armor. + if (item is Weapon weapon) + { + // The item is a weapon, + // maybe you need to equip it in the hand + } + else if (item is Armor armor) + { + // you might want to equip armor in the body + } + } + + [ServerRpc] + void ServerEquipArmor(Armor armor) + { + // IMPORTANT: this does not work either, you will receive an armor, but + // the armor will not have a valid Item.name, even if you passed an armor with name + } + } + // CodeEmbed-End: polymorphic-equip +} + +namespace Mirage.Snippets.Serialization.PolymorphicSerializer +{ + using PolymorphicEquip; + + // CodeEmbed-Start: polymorphic-serializer + public static class ItemSerializer + { + const byte WEAPON = 1; + const byte ARMOR = 2; + + public static void WriteItem(this NetworkWriter writer, Item item) + { + if (item is Weapon weapon) + { + writer.WriteByte(WEAPON); + writer.WriteString(weapon.name); + writer.WritePackedInt32(weapon.hitPoints); + } + else if (item is Armor armor) + { + writer.WriteByte(ARMOR); + writer.WriteString(armor.name); + writer.WritePackedInt32(armor.hitPoints); + writer.WritePackedInt32(armor.level); + } + } + + public static Item ReadItem(this NetworkReader reader) + { + byte type = reader.ReadByte(); + switch(type) + { + case WEAPON: + return new Weapon + { + name = reader.ReadString(), + hitPoints = reader.ReadPackedInt32() + }; + case ARMOR: + return new Armor + { + name = reader.ReadString(), + hitPoints = reader.ReadPackedInt32(), + level = reader.ReadPackedInt32() + }; + default: + throw new Exception($"Invalid weapon type {type}"); + } + } + } + // CodeEmbed-End: polymorphic-serializer +} + +namespace Mirage.Snippets.Serialization.ScriptableObjectSerializer +{ + // CodeEmbed-Start: scriptable-object-serializer + [CreateAssetMenu(fileName = "New Armor", menuName = "Armor Data")] + class Armor : ScriptableObject + { + public int Hitpoints; + public int Weight; + public string Description; + public Texture2D Icon; + // ... + } + + public static class ArmorSerializer + { + public static void WriteArmor(this NetworkWriter writer, Armor armor) + { + // No need to serialize the data, just the name of the armor. + writer.WriteString(armor.name); + } + + public static Armor ReadArmor(this NetworkReader reader) + { + // Load the same armor by name. The data will come from the asset in Resources folder. + return Resources.Load(reader.ReadString()); + } + } + // CodeEmbed-End: scriptable-object-serializer +} diff --git a/Assets/Mirage/Samples~/Snippets/Serialization/DataTypesSnippets.cs.meta b/Assets/Mirage/Samples~/Snippets/Serialization/DataTypesSnippets.cs.meta new file mode 100644 index 00000000000..5f26b0541d3 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Serialization/DataTypesSnippets.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5ca2aa05c34d9b741bbd97bf493861b6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Serialization/GenericsSnippets.cs b/Assets/Mirage/Samples~/Snippets/Serialization/GenericsSnippets.cs new file mode 100644 index 00000000000..dc980024666 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Serialization/GenericsSnippets.cs @@ -0,0 +1,79 @@ +using UnityEngine; +using Mirage; +using Mirage.Serialization; +using Mirage.Collections; + +namespace Mirage.Snippets.Serialization.Generics +{ + // CodeEmbed-Start: generic-behaviour + public class MyGenericBehaviour : NetworkBehaviour + { + [SyncVar] + public T Value { get; set; } + + public void MyRpc(T value) + { + // do stuff + } + } + // CodeEmbed-End: generic-behaviour + + // CodeEmbed-Start: custom-type + [NetworkMessage] + public struct MyCustomType + { + public int Value; + } + // CodeEmbed-End: custom-type + + // CodeEmbed-Start: custom-type-extensions + public static class MyCustomTypeExtensions + { + public static void Write(this NetworkWriter writer, MyCustomType value) + { + // write here + } + + public static MyCustomType Read(this NetworkReader reader) + { + // read here + return default; + } + } + // CodeEmbed-End: custom-type-extensions + + // CodeEmbed-Start: generic-message + public struct MyMessage + { + public T Value; + } + + class Manager + { + public NetworkServer Server; + + void Start() + { + Server.MessageHandler.RegisterHandler>(HandleIntMessage); + } + + void HandleIntMessage(INetworkPlayer player, MyMessage msg) + { + // do stuff + } + } + // CodeEmbed-End: generic-message + + // CodeEmbed-Start: generic-collections + public struct MyType + { + public bool Option; + public T Value; + } + + public class MyBehaviour : NetworkBehaviour + { + public SyncList> myList = new SyncList>(); + } + // CodeEmbed-End: generic-collections +} diff --git a/Assets/Mirage/Samples~/Snippets/Serialization/GenericsSnippets.cs.meta b/Assets/Mirage/Samples~/Snippets/Serialization/GenericsSnippets.cs.meta new file mode 100644 index 00000000000..081f530baa8 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Serialization/GenericsSnippets.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a5673e064e713b84898cbdd0b223face +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Serialization/MyDataStruct.cs b/Assets/Mirage/Samples~/Snippets/Serialization/MyDataStruct.cs new file mode 100644 index 00000000000..f08991700f3 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Serialization/MyDataStruct.cs @@ -0,0 +1,10 @@ +namespace Mirage.Snippets.Serialization +{ + // CodeEmbed-Start: my-data + public struct MyData + { + public int someValue; + public float anotherValue; + } + // CodeEmbed-End: my-data +} diff --git a/Assets/Mirage/Samples~/Snippets/Serialization/MyDataStruct.cs.meta b/Assets/Mirage/Samples~/Snippets/Serialization/MyDataStruct.cs.meta new file mode 100644 index 00000000000..a9f85abfe6e --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Serialization/MyDataStruct.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f0e6363a46fcb1e42981e4f8cd278f95 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Serialization/PropertiesExample.cs b/Assets/Mirage/Samples~/Snippets/Serialization/PropertiesExample.cs new file mode 100644 index 00000000000..dd99c22a58a --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Serialization/PropertiesExample.cs @@ -0,0 +1,32 @@ +using Mirage.Serialization; + +namespace Mirage.Snippets.Serialization.PropertiesExample +{ + // CodeEmbed-Start: properties-example + public struct MyData + { + public int someValue { get; private set; } + public float anotherValue { get; private set; } + + public MyData(int someValue, float anotherValue) + { + this.someValue = someValue; + this.anotherValue = anotherValue; + } + } + + public static class CustomReadWriteFunctions + { + public static void WriteMyType(this NetworkWriter writer, MyData value) + { + writer.WriteInt32(value.someValue); + writer.WriteSingle(value.anotherValue); + } + + public static MyData ReadMyType(this NetworkReader reader) + { + return new MyData(reader.ReadInt32(), reader.ReadSingle()); + } + } + // CodeEmbed-End: properties-example +} diff --git a/Assets/Mirage/Samples~/Snippets/Serialization/PropertiesExample.cs.meta b/Assets/Mirage/Samples~/Snippets/Serialization/PropertiesExample.cs.meta new file mode 100644 index 00000000000..bf4c160a315 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Serialization/PropertiesExample.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 83b4ea109a6f4f245bbe6318284016f2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Serialization/StringStoreSnippets.cs b/Assets/Mirage/Samples~/Snippets/Serialization/StringStoreSnippets.cs new file mode 100644 index 00000000000..06ef8467352 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Serialization/StringStoreSnippets.cs @@ -0,0 +1,127 @@ +using System; +using UnityEngine; +using Mirage; +using Mirage.Serialization; +using Mirage.Serialization.BrotliCompression; + +namespace Mirage.Snippets.Serialization.StringStoreSnippets +{ + // CodeEmbed-Start: mission-example + public class Mission + { + public void OnSerialize(NetworkWriter writer) + { + // serialize mission here + } + + public void OnDeserialize(NetworkReader reader) + { + // deserialize mission here + } + } + + public static class MissionExtensions + { + public static void WriteMission(this NetworkWriter finalWriter, Mission mission) + { + // Get a temporary writer from the pool + using (PooledNetworkWriter innerWriter = NetworkWriterPool.GetWriter()) + { + // Create a new store and attach it to the temporary writer + StringStore stringStore = new StringStore(); + innerWriter.StringStore = stringStore; + + // Write the mission data. + // Any repeated strings (like Objective titles or NPC names) + // will be indexed in 'stringStore'. + mission.OnSerialize(innerWriter); + + // Write the populated store to the REAL writer first + finalWriter.WriteStringStore(stringStore); + + // Write the actual message data as a segment + finalWriter.WriteBytesAndSizeSegment(innerWriter.ToArraySegment()); + } + } + + public static Mission ReadMission(this NetworkReader finalReader) + { + // Read the StringStore that was sent first + StringStore stringStore = finalReader.ReadStringStore(); + + // Read the data segment containing the mission + ArraySegment segment = finalReader.ReadBytesAndSizeSegment(); + + // Get a pooled reader for the segment and attach the store + using (PooledNetworkReader innerReader = NetworkReaderPool.GetReader(segment, null)) + { + innerReader.StringStore = stringStore; + + // 4. Deserialize the mission. + // ReadString calls will now correctly resolve indices using the store. + var mission = new Mission(); + mission.OnDeserialize(innerReader); + return mission; + } + } + } + // CodeEmbed-End: mission-example + + // CodeEmbed-Start: brotli-example + // SERVER: Compressing and caching + public class WorldServer : MonoBehaviour + { + // keep the StringStoreBrotliEncoder (and its results) so that it can be sent to new players + // this is to avoid heavy cpu encoding every time a new player joins + private StringStoreBrotliEncoder _worldEncoder; + + public void InitializeWorld(WorldData world) + { + StringStore store = new StringStore(); + // ... populate store by writing world data to a temp writer ... + + // Create the encoder once. This performs the heavy compression logic. + _worldEncoder = StringStoreBrotliEncoder.Encode(store); + } + + public void OnPlayerJoin(INetworkPlayer player) + { + // Send the pre-compressed payloads to the player. + // This is very fast as it just sends cached byte segments. + _worldEncoder.Send(player); + } + } + + // CLIENT: Receiving + public class MissionManager : MonoBehaviour + { + private StringStoreBrotliDecoder _decoder; + + public void Start() + { + // Initialize the decoder with the Network Client. + // NOTE: StringStoreBrotliDecoder will only receive 1 set of messages, it will unregister the message handlers after it as received one + _decoder = new StringStoreBrotliDecoder(Client.Instance); + + // Subscribe to the completion event + _decoder.OnReceived += () => + { + Debug.Log("Strings Received! Ready to deserialize mission."); + ProcessMission(_decoder.StringStore); + }; + } + + private void ProcessMission(StringStore store) + { + // Use the store in your Readers as shown in the previous example + } + } + + // Dummy classes to make the example compile + public class WorldData {} + public static class Client + { + public static IMessageReceiver Instance => null; + } + // CodeEmbed-End: brotli-example +} diff --git a/Assets/Mirage/Samples~/Snippets/Serialization/StringStoreSnippets.cs.meta b/Assets/Mirage/Samples~/Snippets/Serialization/StringStoreSnippets.cs.meta new file mode 100644 index 00000000000..2007688f95b --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Serialization/StringStoreSnippets.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 380cf9e48ba9e044fae49eebec4784f8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Serialization/SwitchEnum.cs b/Assets/Mirage/Samples~/Snippets/Serialization/SwitchEnum.cs new file mode 100644 index 00000000000..fee9eaa6002 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Serialization/SwitchEnum.cs @@ -0,0 +1,11 @@ +namespace Mirage.Snippets.Serialization +{ + // CodeEmbed-Start: switch-enum + public enum Switch : byte + { + Left, + Middle, + Right, + } + // CodeEmbed-End: switch-enum +} diff --git a/Assets/Mirage/Samples~/Snippets/Serialization/SwitchEnum.cs.meta b/Assets/Mirage/Samples~/Snippets/Serialization/SwitchEnum.cs.meta new file mode 100644 index 00000000000..c94cf95c941 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Serialization/SwitchEnum.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d6b58eb8ce0820740b07a3bd5e8d82d2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Serialization/UnsupportedTypeExample.cs b/Assets/Mirage/Samples~/Snippets/Serialization/UnsupportedTypeExample.cs new file mode 100644 index 00000000000..ba64c5c1479 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Serialization/UnsupportedTypeExample.cs @@ -0,0 +1,67 @@ +using UnityEngine; +using Mirage.Serialization; + +namespace Mirage.Snippets.Serialization.UnsupportedType +{ + // CodeEmbed-Start: collision-example + public struct MyCollision + { + public Vector3 force; + public Rigidbody rigidbody; + } + + public static class CustomReadWriteFunctions + { + public static void WriteMyCollision(this NetworkWriter writer, MyCollision value) + { + writer.WriteVector3(value.force); + + NetworkIdentity networkIdentity = value.rigidbody.GetComponent(); + writer.WriteNetworkIdentity(networkIdentity); + } + + public static MyCollision ReadMyCollision(this NetworkReader reader) + { + Vector3 force = reader.ReadVector3(); + + NetworkIdentity networkIdentity = reader.ReadNetworkIdentity(); + Rigidbody rigidBody = networkIdentity != null + ? networkIdentity.GetComponent() + : null; + + return new MyCollision + { + force = force, + rigidbody = rigidBody, + }; + } + } + // CodeEmbed-End: collision-example +} + +namespace Mirage.Snippets.Serialization.RigidbodyExample +{ + using UnityEngine; + using Mirage.Serialization; + + // CodeEmbed-Start: rigidbody-example + public static class CustomReadWriteFunctions + { + public static void WriteRigidbody(this NetworkWriter writer, Rigidbody rigidbody) + { + NetworkIdentity networkIdentity = rigidbody.GetComponent(); + writer.WriteNetworkIdentity(networkIdentity); + } + + public static Rigidbody ReadRigidbody(this NetworkReader reader) + { + NetworkIdentity networkIdentity = reader.ReadNetworkIdentity(); + Rigidbody rigidBody = networkIdentity != null + ? networkIdentity.GetComponent() + : null; + + return rigidBody; + } + } + // CodeEmbed-End: rigidbody-example +} diff --git a/Assets/Mirage/Samples~/Snippets/Serialization/UnsupportedTypeExample.cs.meta b/Assets/Mirage/Samples~/Snippets/Serialization/UnsupportedTypeExample.cs.meta new file mode 100644 index 00000000000..7f3d94dea78 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Serialization/UnsupportedTypeExample.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1c7d37933661e6c408f7ef62f25f91d0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/UsingSyncPrefab.cs b/Assets/Mirage/Samples~/Snippets/Serialization/UsingSyncPrefab.cs similarity index 95% rename from Assets/Mirage/Samples~/Snippets/UsingSyncPrefab.cs rename to Assets/Mirage/Samples~/Snippets/Serialization/UsingSyncPrefab.cs index 32792cdd3b3..dc6995ed7d7 100644 --- a/Assets/Mirage/Samples~/Snippets/UsingSyncPrefab.cs +++ b/Assets/Mirage/Samples~/Snippets/Serialization/UsingSyncPrefab.cs @@ -1,6 +1,6 @@ using UnityEngine; -namespace Mirage.Snippets.UsingSyncPrefab +namespace Mirage.Snippets.Serialization { // CodeEmbed-Start: shoot public class Shooter : NetworkBehaviour diff --git a/Assets/Mirage/Samples~/Snippets/UsingSyncPrefab.cs.meta b/Assets/Mirage/Samples~/Snippets/Serialization/UsingSyncPrefab.cs.meta similarity index 83% rename from Assets/Mirage/Samples~/Snippets/UsingSyncPrefab.cs.meta rename to Assets/Mirage/Samples~/Snippets/Serialization/UsingSyncPrefab.cs.meta index ca29df44dbb..1d3388b01b3 100644 --- a/Assets/Mirage/Samples~/Snippets/UsingSyncPrefab.cs.meta +++ b/Assets/Mirage/Samples~/Snippets/Serialization/UsingSyncPrefab.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 073de80f4c165e04484631e6c05faff3 +guid: a393048bdc4b3e44eb55bc4071ed0022 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/Mirage/Samples~/Snippets/Spawning.meta b/Assets/Mirage/Samples~/Snippets/Spawning.meta new file mode 100644 index 00000000000..1572511f1b8 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Spawning.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 273a4e8362947fb4488841218e0d949f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/DynamicSpawning.cs b/Assets/Mirage/Samples~/Snippets/Spawning/DynamicSpawning.cs similarity index 100% rename from Assets/Mirage/Samples~/Snippets/DynamicSpawning.cs rename to Assets/Mirage/Samples~/Snippets/Spawning/DynamicSpawning.cs diff --git a/Assets/Mirage/Samples~/Snippets/DynamicSpawning.cs.meta b/Assets/Mirage/Samples~/Snippets/Spawning/DynamicSpawning.cs.meta similarity index 83% rename from Assets/Mirage/Samples~/Snippets/DynamicSpawning.cs.meta rename to Assets/Mirage/Samples~/Snippets/Spawning/DynamicSpawning.cs.meta index 686e6747200..7d1c1afcdee 100644 --- a/Assets/Mirage/Samples~/Snippets/DynamicSpawning.cs.meta +++ b/Assets/Mirage/Samples~/Snippets/Spawning/DynamicSpawning.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: bc8407ad37ce71344a3f790dd86392c1 +guid: c722e00bb9bbe764e9a0da53e03e61b3 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/Mirage/Samples~/Snippets/Sync.meta b/Assets/Mirage/Samples~/Snippets/Sync.meta new file mode 100644 index 00000000000..9af17c21218 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Sync.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5da39ed5b0ad91e4a87c51d5aa4b1596 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Sync/CodeGeneration.cs b/Assets/Mirage/Samples~/Snippets/Sync/CodeGeneration.cs new file mode 100644 index 00000000000..13f7f16425e --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Sync/CodeGeneration.cs @@ -0,0 +1,126 @@ +using Mirage; +using Mirage.Serialization; + +namespace Mirage.Snippets.Sync +{ + // CodeEmbed-Start: CodeGenerationExample + public class Data : NetworkBehaviour + { + [SyncVar(hook = nameof(OnInt1Changed))] + public int int1 { get; set; } = 66; + + [SyncVar] + public int int2 { get; set; } = 23487; + + [SyncVar] + public string MyString { get; set; } = "Example string"; + + void OnInt1Changed(int oldValue, int newValue) + { + // do something here + } + } + // CodeEmbed-End: CodeGenerationExample + + public class GeneratedCodeExample : NetworkBehaviour + { + public int int1; + public int int2; + public string MyString; + + public void OnInt1Changed(int oldValue, int newValue) + { + } + + // CodeEmbed-Start: SerializeSyncVarsExample + public override bool SerializeSyncVars(NetworkWriter writer, bool initialState) + { + // Write any SyncVars in base class + bool written = base.SerializeSyncVars(writer, initialState); + + if (initialState) + { + // The first time a game object is sent to a client, send all the data (and no dirty bits) + writer.WritePackedUInt32((uint)this.int1); + writer.WritePackedUInt32((uint)this.int2); + writer.Write(this.MyString); + return true; + } + else + { + // Writes which SyncVars have changed + writer.WritePackedUInt64(base.SyncVarDirtyBits); + + if ((base.SyncVarDirtyBits & 1u) != 0u) + { + writer.WritePackedUInt32((uint)this.int1); + written = true; + } + + if ((base.SyncVarDirtyBits & 2u) != 0u) + { + writer.WritePackedUInt32((uint)this.int2); + written = true; + } + + if ((base.SyncVarDirtyBits & 4u) != 0u) + { + writer.Write(this.MyString); + written = true; + } + + return written; + } + } + // CodeEmbed-End: SerializeSyncVarsExample + + // CodeEmbed-Start: DeserializeSyncVarsExample + public override void DeserializeSyncVars(NetworkReader reader, bool initialState) + { + // Read any SyncVars in base class + base.DeserializeSyncVars(reader, initialState); + + if (initialState) + { + // The first time a game object is sent to a client, read all the data (and no dirty bits) + int oldInt1 = this.int1; + this.int1 = (int)reader.ReadPackedUInt32(); + // if old and new values are not equal, call hook + if (!base.SyncVarEqual(oldInt1, this.int1)) + { + this.OnInt1Changed(oldInt1, this.int1); + } + + this.int2 = (int)reader.ReadPackedUInt32(); + this.MyString = reader.ReadString(); + return; + } + + int dirtySyncVars = (int)reader.ReadPackedUInt32(); + // is 1st SyncVar dirty + if ((dirtySyncVars & 1) != 0) + { + int oldInt1 = this.int1; + this.int1 = (int)reader.ReadPackedUInt32(); + // if old and new values are not equal, call hook + if (!base.SyncVarEqual(oldInt1, this.int1)) + { + this.OnInt1Changed(oldInt1, this.int1); + } + } + + // is 2nd SyncVar dirty + if ((dirtySyncVars & 2) != 0) + { + this.int2 = (int)reader.ReadPackedUInt32(); + } + + // is 3rd SyncVar dirty + if ((dirtySyncVars & 4) != 0) + { + this.MyString = reader.ReadString(); + } + } + // CodeEmbed-End: DeserializeSyncVarsExample + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Sync/CodeGeneration.cs.meta b/Assets/Mirage/Samples~/Snippets/Sync/CodeGeneration.cs.meta new file mode 100644 index 00000000000..ed60445bf42 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Sync/CodeGeneration.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4b157259e8bf02745ba6f1781634302c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Sync/CustomSerialization.cs b/Assets/Mirage/Samples~/Snippets/Sync/CustomSerialization.cs new file mode 100644 index 00000000000..2d90c81133c --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Sync/CustomSerialization.cs @@ -0,0 +1,16 @@ +using Mirage; +using Mirage.Serialization; + +namespace Mirage.Snippets.Sync +{ + public class CustomSerializationExample + { + // CodeEmbed-Start: OnSerializeSignature + public virtual bool OnSerialize(NetworkWriter writer, bool initialState) => false; + // CodeEmbed-End: OnSerializeSignature + + // CodeEmbed-Start: OnDeserializeSignature + public virtual void OnDeserialize(NetworkReader reader, bool initialState) { } + // CodeEmbed-End: OnDeserializeSignature + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Sync/CustomSerialization.cs.meta b/Assets/Mirage/Samples~/Snippets/Sync/CustomSerialization.cs.meta new file mode 100644 index 00000000000..2e7f8da5a92 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Sync/CustomSerialization.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e2fd8caed3dfc3e46b6836009907f060 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Sync/SyncDictionaryExamples.cs b/Assets/Mirage/Samples~/Snippets/Sync/SyncDictionaryExamples.cs new file mode 100644 index 00000000000..987bd21920d --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Sync/SyncDictionaryExamples.cs @@ -0,0 +1,75 @@ +using System.Collections.Generic; +using UnityEngine; +using Mirage; +using Mirage.Collections; + +namespace Mirage.Snippets.Sync +{ + // CodeEmbed-Start: SyncDictionaryBasicExample + [System.Serializable] + public struct Item + { + public string name; + public int hitPoints; + public int durability; + } + + public class Player : NetworkBehaviour + { + public readonly SyncDictionary equipment = new SyncDictionary(); + + private void Awake() + { + Identity.OnStartServer.AddListener(OnStartServer); + } + + private void OnStartServer() + { + equipment.Add("head", new Item { name = "Helmet", hitPoints = 10, durability = 20 }); + equipment.Add("body", new Item { name = "Epic Armor", hitPoints = 50, durability = 50 }); + equipment.Add("feet", new Item { name = "Sneakers", hitPoints = 3, durability = 40 }); + equipment.Add("hands", new Item { name = "Sword", hitPoints = 30, durability = 15 }); + } + } + // CodeEmbed-End: SyncDictionaryBasicExample + + // CodeEmbed-Start: SyncDictionaryCallbackExample + public class PlayerWithCallbacks : NetworkBehaviour + { + public readonly SyncDictionary equipment = new SyncDictionary(); + public readonly SyncDictionary hotbar = new SyncDictionary(); + + // This will hook the callback on both server and client + private void Awake() + { + equipment.OnChange += UpdateEquipment; + Identity.OnStartClient.AddListener(OnStartClient); + } + + // Hotbar changes will only be invoked on clients + private void OnStartClient() + { + hotbar.OnChange += UpdateHotbar; + } + + private void UpdateEquipment() + { + // Here you can refresh your UI for instance + } + + private void UpdateHotbar() + { + // Here you can refresh your UI for instance + } + } + // CodeEmbed-End: SyncDictionaryCallbackExample + + public class MyDictionary : Dictionary {} + + public class CustomDictionaryExample + { + // CodeEmbed-Start: SyncDictionaryCustomImplementation + public SyncIDictionary myDict = new SyncIDictionary(new MyDictionary()); + // CodeEmbed-End: SyncDictionaryCustomImplementation + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Sync/SyncDictionaryExamples.cs.meta b/Assets/Mirage/Samples~/Snippets/Sync/SyncDictionaryExamples.cs.meta new file mode 100644 index 00000000000..cbc355e5936 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Sync/SyncDictionaryExamples.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e662b1b9eea95cd4793fa40cf3d80a40 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Sync/SyncHashSetExamples.cs b/Assets/Mirage/Samples~/Snippets/Sync/SyncHashSetExamples.cs new file mode 100644 index 00000000000..f835801be7b --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Sync/SyncHashSetExamples.cs @@ -0,0 +1,67 @@ +using UnityEngine; +using Mirage; +using Mirage.Collections; + +namespace Mirage.Snippets.Sync.HashSets.Basic +{ + // CodeEmbed-Start: SyncHashSetBasicExample + [System.Serializable] + public class SyncSkillSet : SyncHashSet {} + + public class Player : NetworkBehaviour + { + [SerializeField] + readonly SyncSkillSet skills = new SyncSkillSet(); + + int skillPoints = 10; + + [Command] + public void CmdLearnSkill(string skillName) + { + if (skillPoints > 1) + { + skillPoints--; + + skills.Add(skillName); + } + } + } + // CodeEmbed-End: SyncHashSetBasicExample +} + +namespace Mirage.Snippets.Sync.HashSets.Callbacks +{ + // CodeEmbed-Start: SyncHashSetCallbackExample + [System.Serializable] + public class SyncSetBuffs : SyncHashSet {} + + public class Player : NetworkBehaviour + { + [SerializeField] + public readonly SyncSetBuffs buffs = new SyncSetBuffs(); + + // this will add the delegate on the client. + // Use OnStartServer instead if you want it on the server + public override void OnStartClient() + { + buffs.Callback += OnBuffsChanged; + } + + private void OnBuffsChanged(SyncSetBuffs.Operation op, string buff) + { + switch (op) + { + case SyncSetBuffs.Operation.OP_ADD: + // we added a buff, draw an icon on the character + break; + case SyncSetBuffs.Operation.OP_CLEAR: + // clear all buffs from the character + break; + case SyncSetBuffs.Operation.OP_REMOVE: + // We removed a buff from the character + break; + } + } + } + // CodeEmbed-End: SyncHashSetCallbackExample +} diff --git a/Assets/Mirage/Samples~/Snippets/Sync/SyncHashSetExamples.cs.meta b/Assets/Mirage/Samples~/Snippets/Sync/SyncHashSetExamples.cs.meta new file mode 100644 index 00000000000..ff704369130 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Sync/SyncHashSetExamples.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7fc9969e7f46e8749b1091885f976b03 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Sync/SyncListExamples.cs b/Assets/Mirage/Samples~/Snippets/Sync/SyncListExamples.cs new file mode 100644 index 00000000000..cc813c2b0e1 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Sync/SyncListExamples.cs @@ -0,0 +1,92 @@ +using UnityEngine; +using Mirage; +using Mirage.Collections; +using System.Collections.Generic; + +namespace Mirage.Snippets.Sync.Lists.Basic +{ + // CodeEmbed-Start: SyncListBasicExample + [System.Serializable] + public struct Item + { + public string name; + public int amount; + public Color32 color; + } + + public class Player : NetworkBehaviour + { + private readonly SyncList inventory = new SyncList(); + + public int coins = 100; + + [ServerRpc] + public void Purchase(string itemName) + { + if (coins > 10) + { + coins -= 10; + Item item = new Item + { + name = "Sword", + amount = 3, + color = new Color32(125, 125, 125, 255) + }; + + // During next synchronization, all clients will see the item + inventory.Add(item); + } + } + } + // CodeEmbed-End: SyncListBasicExample +} + +namespace Mirage.Snippets.Sync.Lists.Callbacks +{ + using Mirage.Snippets.Sync.Lists.Basic; + + // CodeEmbed-Start: SyncListCallbackExample + public class Player : NetworkBehaviour + { + private readonly SyncList inventory = new SyncList(); + private readonly SyncList hotbar = new SyncList(); + + // This will hook the callback on both server and client + private void Awake() + { + inventory.OnChange += UpdateInventory; + Identity.OnStartClient.AddListener(OnStartClient); + } + + // Hotbar changes will only be invoked on clients + private void OnStartClient() + { + hotbar.OnChange += UpdateHotbar; + } + + private void UpdateInventory() + { + // Here you can refresh your UI for instance + } + + private void UpdateHotbar() + { + // Here you can refresh your UI for instance + } + } + // CodeEmbed-End: SyncListCallbackExample +} + +namespace Mirage.Snippets.Sync.Lists.Custom +{ + using Mirage.Snippets.Sync.Lists.Basic; + + public class MyIList : List {} + + public class CustomListExample + { + // CodeEmbed-Start: SyncListCustomImplementation + public SyncList myList = new SyncList(new MyIList()); + // CodeEmbed-End: SyncListCustomImplementation + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Sync/SyncListExamples.cs.meta b/Assets/Mirage/Samples~/Snippets/Sync/SyncListExamples.cs.meta new file mode 100644 index 00000000000..ad83dc02465 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Sync/SyncListExamples.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d69e8a82c8939cc4f955b27e4c32ea22 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Sync/SyncSortedSetExamples.cs b/Assets/Mirage/Samples~/Snippets/Sync/SyncSortedSetExamples.cs new file mode 100644 index 00000000000..c2cf0041351 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Sync/SyncSortedSetExamples.cs @@ -0,0 +1,63 @@ +using UnityEngine; +using Mirage; +using Mirage.Collections; + +namespace Mirage.Snippets.Sync.SortedSets.Basic +{ + // CodeEmbed-Start: SyncSortedSetBasicExample + class Player : NetworkBehaviour + { + class SyncSkillSet : SyncSortedSet {} + + readonly SyncSkillSet skills = new SyncSkillSet(); + + int skillPoints = 10; + + [Command] + public void CmdLearnSkill(string skillName) + { + if (skillPoints > 1) + { + skillPoints--; + + skills.Add(skillName); + } + } + } + // CodeEmbed-End: SyncSortedSetBasicExample +} + +namespace Mirage.Snippets.Sync.SortedSets.Callbacks +{ + // CodeEmbed-Start: SyncSortedSetCallbackExample + public class Player : NetworkBehaviour + { + private class SyncSetBuffs : SyncSortedSet {} + + private readonly SyncSetBuffs buffs = new SyncSetBuffs(); + + // This will add the delegate on the client. + // Use OnStartServer instead if you want it on the server + public override void OnStartClient() + { + buffs.Callback += OnBuffsChanged; + } + + private void OnBuffsChanged(SyncSetBuffs.Operation op, string buff) + { + switch (op) + { + case SyncSetBuffs.Operation.OP_ADD: + // we added a buff, draw an icon on the character + break; + case SyncSetBuffs.Operation.OP_CLEAR: + // clear all buffs from the character + break; + case SyncSetBuffs.Operation.OP_REMOVE: + // We removed a buff from the character + break; + } + } + } + // CodeEmbed-End: SyncSortedSetCallbackExample +} diff --git a/Assets/Mirage/Samples~/Snippets/Sync/SyncSortedSetExamples.cs.meta b/Assets/Mirage/Samples~/Snippets/Sync/SyncSortedSetExamples.cs.meta new file mode 100644 index 00000000000..c5f68a75bc5 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Sync/SyncSortedSetExamples.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b6f02ab3c84205545ac8d0357383fb9c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Sync/SyncVarExamples.cs b/Assets/Mirage/Samples~/Snippets/Sync/SyncVarExamples.cs new file mode 100644 index 00000000000..71e788349ac --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Sync/SyncVarExamples.cs @@ -0,0 +1,188 @@ +using UnityEngine; +using Mirage; + +namespace Mirage.Snippets.Sync.Vars.Basic +{ + // CodeEmbed-Start: SyncVarBasicExample + public class Player : NetworkBehaviour + { + [SyncVar] + public int clickCount { get; set; } + + private void Update() + { + if (IsLocalPlayer && Input.GetMouseButtonDown(0)) + ServerRpc_IncreaseClicks(); + } + + [ServerRpc] + public void ServerRpc_IncreaseClicks() + { + // This is executed on the server + clickCount++; + } + } + // CodeEmbed-End: SyncVarBasicExample +} + +namespace Mirage.Snippets.Sync.Vars.Inheritance +{ + public class ClassInheritanceExample + { + // CodeEmbed-Start: SyncVarInheritanceExample + private class Pet : NetworkBehaviour + { + [SyncVar] + private string name { get; set; } + } + + private class Cat : Pet + { + [SyncVar] + private Color32 color { get; set; } + } + // CodeEmbed-End: SyncVarInheritanceExample + } +} + +namespace Mirage.Snippets.Sync.Vars.ClientOnlyHook +{ + // CodeEmbed-Start: SyncVarClientOnlyHookExample + public class Player : NetworkBehaviour + { + [SyncVar(hook = nameof(UpdateColor))] + private Color playerColor { get; set; } = Color.black; + + private Renderer renderer; + + // Unity makes a clone of the Material every time renderer.material is used. + // Cache it here and Destroy it in OnDestroy to prevent a memory leak. + private Material cachedMaterial; + + private void Awake() + { + renderer = GetComponent(); + Identity.OnStartServer.AddListener(OnStartServer); + } + + private void OnStartServer() + { + playerColor = Random.ColorHSV(0f, 1f, 1f, 1f, 0.5f, 1f); + } + + private void UpdateColor(Color oldColor, Color newColor) + { + // this is executed on this player for each client + if (cachedMaterial == null) + cachedMaterial = renderer.material; + + cachedMaterial.color = newColor; + } + + private void OnDestroy() + { + Destroy(cachedMaterial); + } + } + // CodeEmbed-End: SyncVarClientOnlyHookExample +} + +namespace Mirage.Snippets.Sync.Vars.ServerClientHook +{ + // CodeEmbed-Start: SyncVarServerClientHookExample + public class Player : NetworkBehaviour + { + [SyncVar(hook = nameof(UpdateColor), invokeHookOnServer = true)] + private Color playerColor { get; set; } = Color.black; + + private Renderer renderer; + + // Unity makes a clone of the Material every time renderer.material is used. + // Cache it here and Destroy it in OnDestroy to prevent a memory leak. + private Material cachedMaterial; + + private void Awake() + { + renderer = GetComponent(); + Identity.OnStartServer.AddListener(OnStartServer); + } + + private void OnStartServer() + { + playerColor = Random.ColorHSV(0f, 1f, 1f, 1f, 0.5f, 1f); + } + + private void UpdateColor(Color oldColor, Color newColor) + { + // this is executed on this player for each client + if (cachedMaterial == null) + cachedMaterial = renderer.material; + + cachedMaterial.color = newColor; + } + + private void OnDestroy() + { + Destroy(cachedMaterial); + } + } + // CodeEmbed-End: SyncVarServerClientHookExample +} + +namespace Mirage.Snippets.Sync.Vars.InitialOnly +{ + // CodeEmbed-Start: SyncVarInitialOnlyExample + public class Player : NetworkBehaviour + { + [SyncVar(initialOnly = true)] + private int weaponId { get; set; } + + private void Awake() + { + Identity.OnStartClient.AddListener(OnStartClient); + } + + private void OnStartClient() + { + // Update weapon using id from syncvar (sent to client via spawn message + UpdateWeapon(weaponId); + } + + private void Update() + { + // Client Request weapon change + if (Input.GetKeyDown(KeyCode.Q)) + ServerRpc_SetSyncVarWeaponId(7); + } + + [ServerRpc] + private void ServerRpc_SetSyncVarWeaponId(int weaponId) + { + // Set weapon id on server so new players get it + this.weaponId = weaponId; + + // Tell current players about it + ClientRpc_SetSyncVarWeaponId(weaponId); + + // Update weapon on server + UpdateWeapon(weaponId); + } + + [ClientRpc] + private void ClientRpc_SetSyncVarWeaponId(int weaponId) + { + // Set id on client + this.weaponId = weaponId; + + // Update weapon on client + UpdateWeapon(weaponId); + } + + public void UpdateWeapon(int weaponId) + { + // Do stuff to update weapon here + // For example, its spawning model + } + } + // CodeEmbed-End: SyncVarInitialOnlyExample +} diff --git a/Assets/Mirage/Samples~/Snippets/Sync/SyncVarExamples.cs.meta b/Assets/Mirage/Samples~/Snippets/Sync/SyncVarExamples.cs.meta new file mode 100644 index 00000000000..da014c8c527 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Sync/SyncVarExamples.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0f81d1d28683d924a91b68c47d270fc9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Sync/SyncVarHookExamples.cs b/Assets/Mirage/Samples~/Snippets/Sync/SyncVarHookExamples.cs new file mode 100644 index 00000000000..e6d210ad5c0 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Sync/SyncVarHookExamples.cs @@ -0,0 +1,32 @@ +using System; +using Mirage; + +namespace Mirage.Snippets.Sync.VarHooks +{ + public class SyncVarHookAttributeExample : NetworkBehaviour + { + // CodeEmbed-Start: SyncVarHookAttribute + [SyncVar(hook = nameof(HookName))] + // CodeEmbed-End: SyncVarHookAttribute + public int myValue; + + void HookName(int oldValue, int newValue) {} + } + + public class HookSignaturesExample + { + // CodeEmbed-Start: HookSignatures + void hook0() { } + + void hook1(int newValue) { } + + void hook2(int oldValue, int newValue) { } + + event Action event0; + + event Action event1; + + event Action event2; + // CodeEmbed-End: HookSignatures + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Sync/SyncVarHookExamples.cs.meta b/Assets/Mirage/Samples~/Snippets/Sync/SyncVarHookExamples.cs.meta new file mode 100644 index 00000000000..c1e353a16dc --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Sync/SyncVarHookExamples.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2abd1e7529fd0b64a900f88dbedba026 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/doc/docs/analyzers/MIRAGE1001.md b/doc/docs/analyzers/MIRAGE1001.md index f9dbebdc649..024d185e038 100644 --- a/doc/docs/analyzers/MIRAGE1001.md +++ b/doc/docs/analyzers/MIRAGE1001.md @@ -12,20 +12,7 @@ Class-based types are generally risky for network synchronization because: --- ## Example of Triggering Code -```csharp -public class PlayerData -{ - public int health; - public string name; -} - -public class Player : NetworkBehaviour -{ - // Warns: SyncVar 'data' is a class type 'PlayerData'. - [SyncVar] - public PlayerData data { get; set; } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1001.cs' Name:'mirage1001-triggering' }}} --- @@ -33,38 +20,12 @@ public class Player : NetworkBehaviour ### Option 1: Use a struct (Recommended) Structs (value types) avoid memory allocation, support standard change tracking, and guarantee safe value-copy semantics. -```csharp -public struct PlayerData -{ - public int health; - public string name; -} - -public class Player : NetworkBehaviour -{ - [SyncVar] - public PlayerData data { get; set; } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1001.cs' Name:'mirage1001-struct-option' }}} ### Option 2: Implement Custom Serialization and mark the class as safe If you want to use the class type and manage performance/reference safety yourself, write custom `Write` and `Read` extension methods for the class, and decorate the class with `[WeaverSafeClass]` to suppress the warning globally. -```csharp -[WeaverSafeClass] -public class PlayerData -{ - public int health; - public string name; -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1001.cs' Name:'mirage1001-class-option' }}} ### Option 3: Suppress the warning on the property If you want to disable the warning only on a specific property, decorate the property with `[WeaverSafeClass]`. -```csharp -public class Player : NetworkBehaviour -{ - [SyncVar] - [WeaverSafeClass] - public PlayerData data { get; set; } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1001.cs' Name:'mirage1001-suppress-option' }}} diff --git a/doc/docs/analyzers/MIRAGE1002.md b/doc/docs/analyzers/MIRAGE1002.md index a8c68636505..3098264d0f4 100644 --- a/doc/docs/analyzers/MIRAGE1002.md +++ b/doc/docs/analyzers/MIRAGE1002.md @@ -8,30 +8,11 @@ Mirage's IL Weaver post-processes compiled assemblies by intercepting property w --- ## Example of Triggering Code -```csharp -public class Player : NetworkBehaviour -{ - private int _health; - - // Errors: SyncVar property 'health' must be a non-static auto-property... - [SyncVar] - public int health - { - get => _health; - set => _health = value; - } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1002.cs' Name:'mirage1002-triggering' }}} --- ## How to Resolve Change the property to a standard auto-property with both `get` and `set` accessors. -```csharp -public class Player : NetworkBehaviour -{ - [SyncVar] - public int health { get; set; } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1002.cs' Name:'mirage1002-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1003.md b/doc/docs/analyzers/MIRAGE1003.md index ee877e581d8..4f3ef35bfb5 100644 --- a/doc/docs/analyzers/MIRAGE1003.md +++ b/doc/docs/analyzers/MIRAGE1003.md @@ -8,26 +8,7 @@ Because C# structs are value types, modifying an element's member directly after --- ## Example of Triggering Code -```csharp -using Mirage; -using Mirage.Collections; - -public struct PlayerData -{ - public int health; -} - -public class Player : NetworkBehaviour -{ - public readonly SyncList playerList = new SyncList(); - - public void DamagePlayer(int index, int damage) - { - // Warning: Direct mutation of elements inside playerList is not supported because changes cannot be tracked. - playerList[index].health -= damage; - } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1003.cs' Name:'mirage1003-triggering' }}} --- @@ -35,25 +16,4 @@ public class Player : NetworkBehaviour Retrieve the element, perform the modification, and then assign the modified element back to the collection via the indexer. This ensures the collection's change tracking is triggered. -```csharp -using Mirage; -using Mirage.Collections; - -public struct PlayerData -{ - public int health; -} - -public class Player : NetworkBehaviour -{ - public readonly SyncList playerList = new SyncList(); - - public void DamagePlayer(int index, int damage) - { - // Correct: Modifying the element and setting it back, triggering the index setter - var data = playerList[index]; - data.health -= damage; - playerList[index] = data; - } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1003.cs' Name:'mirage1003-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1004.md b/doc/docs/analyzers/MIRAGE1004.md index 06f9d5faf94..1699c474f97 100644 --- a/doc/docs/analyzers/MIRAGE1004.md +++ b/doc/docs/analyzers/MIRAGE1004.md @@ -8,21 +8,7 @@ Fields implementing `ISyncObject` must be initialized once when the class is con --- ## Example of Triggering Code -```csharp -using Mirage; -using Mirage.Collections; - -public class Player : NetworkBehaviour -{ - // Error: ISyncObject field 'playerList' must be marked readonly and cannot be reassigned - public SyncList playerList = new SyncList(); - - public void ResetList() - { - playerList = new SyncList(); - } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1004.cs' Name:'mirage1004-triggering' }}} --- @@ -30,19 +16,4 @@ public class Player : NetworkBehaviour Mark the field as `readonly` to ensure it cannot be reassigned. To clear or reset the collection, use the collection's `.Clear()` method instead of instantiating a new object. -```csharp -using Mirage; -using Mirage.Collections; - -public class Player : NetworkBehaviour -{ - // Correct: Marked as readonly - public readonly SyncList playerList = new SyncList(); - - public void ResetList() - { - // Correct: Clear the list instead of reassigning it - playerList.Clear(); - } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1004.cs' Name:'mirage1004-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1101.md b/doc/docs/analyzers/MIRAGE1101.md index 1a7c6da3134..17225980352 100644 --- a/doc/docs/analyzers/MIRAGE1101.md +++ b/doc/docs/analyzers/MIRAGE1101.md @@ -8,29 +8,11 @@ These attributes control network synchronization or inject runtime active checks --- ## Example of Triggering Code -```csharp -using UnityEngine; -using Mirage; - -public class GameManager : MonoBehaviour -{ - // Errors: Attribute 'SyncVarAttribute' cannot be used on 'score'... - [SyncVar] - public int score { get; set; } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1101.cs' Name:'mirage1101-triggering' }}} --- ## How to Resolve Ensure that the declaring class inherits from `NetworkBehaviour` instead of `MonoBehaviour` or other base classes. -```csharp -using Mirage; - -public class GameManager : NetworkBehaviour -{ - [SyncVar] - public int score { get; set; } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1101.cs' Name:'mirage1101-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1201.md b/doc/docs/analyzers/MIRAGE1201.md index a2ee97efec8..9d588dac66a 100644 --- a/doc/docs/analyzers/MIRAGE1201.md +++ b/doc/docs/analyzers/MIRAGE1201.md @@ -10,26 +10,7 @@ Remote procedure calls must serialize their arguments and return values across t --- ## Example of Triggering Code -```csharp -using Mirage; -using Cysharp.Threading.Tasks; - -public class Player : NetworkBehaviour -{ - // Errors: RPC method 'CmdTakeDamage' is invalid: cannot have generic parameters. - [ServerRpc] - public void CmdTakeDamage(T damage) - { - } - - // Errors: RPC method 'CmdGetStats' is invalid: cannot return 'PlayerStats'... - [ServerRpc] - public PlayerStats CmdGetStats() - { - return new PlayerStats(); - } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1201.cs' Name:'mirage1201-triggering' }}} --- @@ -38,22 +19,4 @@ public class Player : NetworkBehaviour 1. Make the method non-generic. 2. Ensure the return type is `void` or a valid async task wrapper like `UniTask` (or `UniTask` for asynchronous RPCs). -```csharp -using Mirage; -using Cysharp.Threading.Tasks; - -public class Player : NetworkBehaviour -{ - [ServerRpc] - public void CmdTakeDamage(int damage) - { - } - - [ServerRpc] - public async UniTask CmdGetStats() - { - await UniTask.Yield(); - return new PlayerStats(); - } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1201.cs' Name:'mirage1201-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1202.md b/doc/docs/analyzers/MIRAGE1202.md index b308724914e..2162409e1d7 100644 --- a/doc/docs/analyzers/MIRAGE1202.md +++ b/doc/docs/analyzers/MIRAGE1202.md @@ -8,19 +8,7 @@ RPCs (Remote Procedure Calls) serialize arguments and send them over the network --- ## Example of Triggering Code -```csharp -using Mirage; - -public class Player : NetworkBehaviour -{ - // Error: ServerRpc method 'CmdTakeDamage' cannot have ref/out parameters - [ServerRpc] - public void CmdTakeDamage(ref int health) - { - health -= 10; - } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1202.cs' Name:'mirage1202-triggering' }}} --- @@ -28,19 +16,4 @@ public class Player : NetworkBehaviour Pass parameters by value. If you need to communicate updated state back to the caller, either use an asynchronous RPC with a `UniTask` return value or update a synchronized property (such as a `[SyncVar]`). -```csharp -using Mirage; - -public class Player : NetworkBehaviour -{ - [SyncVar] - public int Health { get; set; } - - // Correct: Pass by value and synchronize via SyncVar - [ServerRpc] - public void CmdTakeDamage(int damage) - { - Health -= damage; - } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1202.cs' Name:'mirage1202-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1203.md b/doc/docs/analyzers/MIRAGE1203.md index 33112d54df4..9c9516431fe 100644 --- a/doc/docs/analyzers/MIRAGE1203.md +++ b/doc/docs/analyzers/MIRAGE1203.md @@ -8,19 +8,7 @@ RPC methods must execute on a specific instance of a `NetworkBehaviour` on a spe --- ## Example of Triggering Code -```csharp -using Mirage; - -public class Player : NetworkBehaviour -{ - // Error: ServerRpc method 'CmdSpawnGlobal' must not be static - [ServerRpc] - public static void CmdSpawnGlobal() - { - // Static context has no NetworkIdentity - } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1203.cs' Name:'mirage1203-triggering' }}} --- @@ -28,16 +16,4 @@ public class Player : NetworkBehaviour Remove the `static` modifier from the RPC method declaration so it runs within the instance context of a spawned `NetworkBehaviour`. -```csharp -using Mirage; - -public class Player : NetworkBehaviour -{ - // Correct: Instance method has access to the NetworkBehaviour state - [ServerRpc] - public void CmdSpawn() - { - // Normal instance context - } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1203.cs' Name:'mirage1203-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1204.md b/doc/docs/analyzers/MIRAGE1204.md index e78dacd9279..eeb6b51e9ac 100644 --- a/doc/docs/analyzers/MIRAGE1204.md +++ b/doc/docs/analyzers/MIRAGE1204.md @@ -10,26 +10,7 @@ Broadcast RPCs (where the target is `Observers`) cannot collect return values si --- ## Example of Triggering Code -```csharp -using Mirage; -using Cysharp.Threading.Tasks; - -public class Player : NetworkBehaviour -{ - // Error: [ClientRpc] must return void when target is Observers. - [ClientRpc(target = RpcTarget.Observers)] - public UniTask RpcGetHealth() - { - return UniTask.FromResult(100); - } - - // Error: ClientRpc method with target = Player requires first parameter to be INetworkPlayer - [ClientRpc(target = RpcTarget.Player)] - public void RpcGiveItem(int itemId) - { - } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1204.cs' Name:'mirage1204-triggering' }}} --- @@ -38,23 +19,4 @@ public class Player : NetworkBehaviour 1. If the RPC returns values, change the target to `RpcTarget.Owner` or `RpcTarget.Player`. 2. If the RPC targets `RpcTarget.Player`, ensure the first parameter is of type `INetworkPlayer` (or `NetworkConnection`). -```csharp -using Mirage; -using Cysharp.Threading.Tasks; - -public class Player : NetworkBehaviour -{ - // Correct: Targeted RPC returning value to the Owner - [ClientRpc(target = RpcTarget.Owner)] - public UniTask RpcGetHealth() - { - return UniTask.FromResult(100); - } - - // Correct: First parameter is the target player connection - [ClientRpc(target = RpcTarget.Player)] - public void RpcGiveItem(INetworkPlayer targetPlayer, int itemId) - { - } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1204.cs' Name:'mirage1204-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1205.md b/doc/docs/analyzers/MIRAGE1205.md index af5901cf17c..e622cda79f6 100644 --- a/doc/docs/analyzers/MIRAGE1205.md +++ b/doc/docs/analyzers/MIRAGE1205.md @@ -11,19 +11,7 @@ Rate limiting buckets require positive numbers for intervals, refill rates, and --- ## Example of Triggering Code -```csharp -using Mirage; - -public class Player : NetworkBehaviour -{ - // Error: RateLimit interval must be greater than zero, and MaxTokens must be >= Refill - [ServerRpc] - [RateLimit(Interval = -0.5f, Refill = 10, MaxTokens = 5)] - public void CmdSpammyAction() - { - } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1205.cs' Name:'mirage1205-triggering' }}} --- @@ -31,16 +19,4 @@ public class Player : NetworkBehaviour Correct the parameters of the `[RateLimit]` attribute to ensure they are positive, valid values. Ensure `MaxTokens` is at least equal to the `Refill` value. -```csharp -using Mirage; - -public class Player : NetworkBehaviour -{ - // Correct: Positive interval and MaxTokens >= Refill - [ServerRpc] - [RateLimit(Interval = 1.0f, Refill = 10, MaxTokens = 20)] - public void CmdSpammyAction() - { - } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1205.cs' Name:'mirage1205-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1206.md b/doc/docs/analyzers/MIRAGE1206.md index 7ec3035e442..15f670543f5 100644 --- a/doc/docs/analyzers/MIRAGE1206.md +++ b/doc/docs/analyzers/MIRAGE1206.md @@ -8,18 +8,7 @@ To prevent denial of service (DoS) attacks, server CPU strain, and memory bloat --- ## Example of Triggering Code -```csharp -using Mirage; - -public class Player : NetworkBehaviour -{ - // Warning: ServerRpc 'CmdFireWeapon' should have a [RateLimit] attribute to prevent spam - [ServerRpc] - public void CmdFireWeapon() - { - } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1206.cs' Name:'mirage1206-triggering' }}} --- @@ -27,16 +16,4 @@ public class Player : NetworkBehaviour Add a `[RateLimit]` attribute to the `[ServerRpc]` method with appropriate parameters for the expected rate of call. -```csharp -using Mirage; - -public class Player : NetworkBehaviour -{ - // Correct: ServerRpc decorated with [RateLimit] to throttle client requests - [ServerRpc] - [RateLimit(Interval = 0.2f, Refill = 5, MaxTokens = 10)] - public void CmdFireWeapon() - { - } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1206.cs' Name:'mirage1206-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1301.md b/doc/docs/analyzers/MIRAGE1301.md index 59cfadae647..8e8c514f332 100644 --- a/doc/docs/analyzers/MIRAGE1301.md +++ b/doc/docs/analyzers/MIRAGE1301.md @@ -12,22 +12,7 @@ Class-based types are generally risky for network messaging because: --- ## Example of Triggering Code -```csharp -using Mirage; - -public class TargetInfo -{ - public int x; - public int y; -} - -[NetworkMessage] -public struct FireMessage -{ - // Warns: NetworkMessage field 'info' is a class type 'TargetInfo'. - public TargetInfo info; -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1301.cs' Name:'mirage1301-triggering' }}} --- @@ -35,38 +20,12 @@ public struct FireMessage ### Option 1: Use a struct (Recommended) Structs (value types) avoid memory allocations and guarantee safe copy-by-value semantics. -```csharp -public struct TargetInfo -{ - public int x; - public int y; -} - -[NetworkMessage] -public struct FireMessage -{ - public TargetInfo info; -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1301.cs' Name:'mirage1301-struct-option' }}} ### Option 2: Implement Custom Serialization and mark the class as safe If you want to use the class type and manage performance/reference safety yourself, write custom `Write` and `Read` extension methods for the class, and decorate the class with `[WeaverSafeClass]` to suppress the warning globally. -```csharp -[WeaverSafeClass] -public class TargetInfo -{ - public int x; - public int y; -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1301.cs' Name:'mirage1301-class-option' }}} ### Option 3: Suppress the warning on the member or parameter If you want to disable the warning only on a specific field, property, or parameter, decorate it with `[WeaverSafeClass]`. -```csharp -[NetworkMessage] -public struct FireMessage -{ - [WeaverSafeClass] - public TargetInfo info; -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1301.cs' Name:'mirage1301-suppress-option' }}} diff --git a/doc/docs/analyzers/MIRAGE1302.md b/doc/docs/analyzers/MIRAGE1302.md index d0492c31dd9..e144bb33641 100644 --- a/doc/docs/analyzers/MIRAGE1302.md +++ b/doc/docs/analyzers/MIRAGE1302.md @@ -8,17 +8,7 @@ Mirage uses compile-time IL weaving to generate serialization code for NetworkMe --- ## Example of Triggering Code -```csharp -using Mirage; -using System.Threading; - -[NetworkMessage] -public struct StartSessionMessage -{ - // Error: Field type 'Thread' is not serializable by Mirage. - public Thread executionThread; -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1302.cs' Name:'mirage1302-triggering' }}} --- @@ -26,13 +16,4 @@ public struct StartSessionMessage Ensure all fields are of serializable types. If you need to send a custom type, make sure it is a struct/class with only serializable fields, or implement custom `Write` and `Read` extension methods for the custom type so that Mirage knows how to serialize it. -```csharp -using Mirage; - -[NetworkMessage] -public struct StartSessionMessage -{ - // Correct: Pass a serializable identifier instead of the raw thread object - public string threadName; -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1302.cs' Name:'mirage1302-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1303.md b/doc/docs/analyzers/MIRAGE1303.md index 29c845a4101..937dc1b6fc4 100644 --- a/doc/docs/analyzers/MIRAGE1303.md +++ b/doc/docs/analyzers/MIRAGE1303.md @@ -12,24 +12,7 @@ If only one of the methods is defined, or if the parameter/return types do not e --- ## Example of Triggering Code -```csharp -using Mirage; -using Mirage.Serialization; - -public struct CustomType -{ - public int value; -} - -public static class CustomSerialization -{ - // Error: Custom writer defined but matching custom reader is missing - public static void WriteCustomType(this NetworkWriter writer, CustomType value) - { - writer.WritePackedInt32(value.value); - } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1303.cs' Name:'mirage1303-triggering' }}} --- @@ -37,26 +20,4 @@ public static class CustomSerialization Provide a matching reader or writer method with the correct signature. Ensure that the type being read and written is exactly the same. -```csharp -using Mirage; -using Mirage.Serialization; - -public struct CustomType -{ - public int value; -} - -public static class CustomSerialization -{ - // Correct: Both writer and reader are defined with matching signatures - public static void WriteCustomType(this NetworkWriter writer, CustomType value) - { - writer.WritePackedInt32(value.value); - } - - public static CustomType ReadCustomType(this NetworkReader reader) - { - return new CustomType { value = reader.ReadPackedInt32() }; - } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1303.cs' Name:'mirage1303-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1401.md b/doc/docs/analyzers/MIRAGE1401.md index f93fdaaedf2..08af7969a7a 100644 --- a/doc/docs/analyzers/MIRAGE1401.md +++ b/doc/docs/analyzers/MIRAGE1401.md @@ -8,24 +8,7 @@ Unity's `Awake` and `Start` methods are called during GameObject initialization. --- ## Example of Triggering Code -```csharp -using Mirage; - -public class PlayerHealth : NetworkBehaviour -{ - [SyncVar] - public int Health { get; set; } - - private void Start() - { - // Warning: Accessing Network State (IsServer/SyncVar) in Start - if (IsServer) - { - Health = 100; - } - } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1401.cs' Name:'mirage1401-triggering' }}} --- @@ -33,18 +16,4 @@ public class PlayerHealth : NetworkBehaviour Override `OnStartServer`, `OnStartClient`, `OnStartLocalPlayer`, or `OnStartAuthority` to run network initialization code when the network state is fully ready. -```csharp -using Mirage; - -public class PlayerHealth : NetworkBehaviour -{ - [SyncVar] - public int Health { get; set; } - - // Correct: Run server initialization when the network server has started - public override void OnStartServer() - { - Health = 100; - } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1401.cs' Name:'mirage1401-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1402.md b/doc/docs/analyzers/MIRAGE1402.md index 148f64fdbb0..9c8a9b82b76 100644 --- a/doc/docs/analyzers/MIRAGE1402.md +++ b/doc/docs/analyzers/MIRAGE1402.md @@ -8,29 +8,7 @@ Derived classes that inherit from another `NetworkBehaviour` which has its own s --- ## Example of Triggering Code -```csharp -using Mirage; -using Mirage.Serialization; - -public class BasePlayer : NetworkBehaviour -{ - [SyncVar] - public string PlayerName { get; set; } -} - -public class HeroPlayer : BasePlayer -{ - [SyncVar] - public int HeroId { get; set; } - - // Warning: Overriding OnSerialize without calling base.OnSerialize - public override bool OnSerialize(NetworkWriter writer, bool initialState) - { - writer.WritePackedInt32(HeroId); - return true; - } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1402.cs' Name:'mirage1402-triggering' }}} --- @@ -38,27 +16,4 @@ public class HeroPlayer : BasePlayer Add the call to `base.OnSerialize` or `base.OnDeserialize` inside the overridden method and combine its return value with the derived class's serialization status. -```csharp -using Mirage; -using Mirage.Serialization; - -public class BasePlayer : NetworkBehaviour -{ - [SyncVar] - public string PlayerName { get; set; } -} - -public class HeroPlayer : BasePlayer -{ - [SyncVar] - public int HeroId { get; set; } - - // Correct: Calls base.OnSerialize and combines dirty states - public override bool OnSerialize(NetworkWriter writer, bool initialState) - { - bool baseDirty = base.OnSerialize(writer, initialState); - writer.WritePackedInt32(HeroId); - return baseDirty || true; - } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1402.cs' Name:'mirage1402-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1501.md b/doc/docs/analyzers/MIRAGE1501.md index 691ed07383f..5d8bcc95417 100644 --- a/doc/docs/analyzers/MIRAGE1501.md +++ b/doc/docs/analyzers/MIRAGE1501.md @@ -8,16 +8,7 @@ If a single message size exceeds the MTU, it must be fragmented at the transport --- ## Example of Triggering Code -```csharp -using Mirage; - -[NetworkMessage] -public struct HugeMessage -{ - // Warning: Array size and primitives exceed the safe MTU threshold - public byte[] largeBuffer; // e.g. filled with 2048 bytes of data -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1501.cs' Name:'mirage1501-triggering' }}} --- @@ -25,14 +16,4 @@ public struct HugeMessage Break large messages down into smaller chunks, use compression, or send raw bulk data using a streaming/chunking API instead of a single massive NetworkMessage. -```csharp -using Mirage; - -[NetworkMessage] -public struct ChunkMessage -{ - public int chunkIndex; - // Correct: Small buffer sizes that fit comfortably within a single MTU packet - public byte[] smallBuffer; // e.g. limited to 512 bytes per chunk -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1501.cs' Name:'mirage1501-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1502.md b/doc/docs/analyzers/MIRAGE1502.md index 5c660c6c73b..6629a042bbe 100644 --- a/doc/docs/analyzers/MIRAGE1502.md +++ b/doc/docs/analyzers/MIRAGE1502.md @@ -8,16 +8,7 @@ Allowing unbounded strings or collections in network messages introduces securit --- ## Example of Triggering Code -```csharp -using Mirage; - -[NetworkMessage] -public struct ChatMessage -{ - // Warning: Unbounded string can be exploited to send megabytes of text - public string text; -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1502.cs' Name:'mirage1502-triggering' }}} --- @@ -25,15 +16,4 @@ public struct ChatMessage Use size-limiting attributes (such as `[BitCount]` or other string/collection size limiters) to restrict the collection size at serialization time, or enforce maximum limits during deserialization (such as setting `MaxDeltaCount` or `MaxElements` on SyncObjects). -```csharp -using Mirage; -using Mirage.Serialization; - -[NetworkMessage] -public struct ChatMessage -{ - // Correct: Restrict the maximum string length using BitCount or other validation attributes - [BitCount(8)] - public string text; -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1502.cs' Name:'mirage1502-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1503.md b/doc/docs/analyzers/MIRAGE1503.md index 060023640fe..8bc8e245584 100644 --- a/doc/docs/analyzers/MIRAGE1503.md +++ b/doc/docs/analyzers/MIRAGE1503.md @@ -8,20 +8,7 @@ Standard uncompressed primitives write their full bit-width (e.g. 32 bits for `i --- ## Example of Triggering Code -```csharp -using Mirage; - -public class Player : NetworkBehaviour -{ - // Warning: 'Health' uses uncompressed int which has high bit-overhead. - [SyncVar] - public int Health { get; set; } - - // Warning: 'PlayerScale' uses uncompressed float which has high bit-overhead. - [SyncVar] - public float PlayerScale { get; set; } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1503.cs' Name:'mirage1503-triggering' }}} --- @@ -29,18 +16,4 @@ public class Player : NetworkBehaviour Decorate the fields/properties with appropriate compression attributes like `[BitCount]`, `[VarInt]`, `[FloatPack]`, or `[BitCountFromRange]` to minimize the serialized bit size. -```csharp -using Mirage; -using Mirage.Serialization; - -public class Player : NetworkBehaviour -{ - // Correct: Restrict Health to 7 bits (0-127 range) - [SyncVar, BitCount(7)] - public int Health { get; set; } - - // Correct: Compress float with a defined range and precision - [SyncVar, FloatPack(-10f, 10f, 0.01f)] - public float PlayerScale { get; set; } -} -``` +{{{ Path:'Snippets/Analyzers/Mirage1503.cs' Name:'mirage1503-resolved' }}} diff --git a/doc/docs/components/network-discovery.md b/doc/docs/components/network-discovery.md index 1ff54ad4389..7ff54f9bc11 100644 --- a/doc/docs/components/network-discovery.md +++ b/doc/docs/components/network-discovery.md @@ -59,65 +59,10 @@ Sometimes you want to provide more information in the discovery messages. Some u The message classes define what is sent between the client and server. As long as you keep your messages simple using the [Data Types](/docs/guides/serialization/data-types) that Mirage can serialize, you won't need to write custom serializers for them. -```cs -public class DiscoveryRequest -{ - public string language="en"; - - // Add properties for whatever information you want sent by clients - // in their broadcast messages that servers will consume. -} - -public class DiscoveryResponse -{ - enum GameMode {PvP, PvE}; - - // you probably want uri so clients know how to connect to the server - public Uri uri; - - public GameMode GameMode; - public int TotalPlayers; - public int HostPlayerName; - - // Add properties for whatever information you want the server to return to - // clients for them to display or consume for establishing a connection. -} -``` +{{{ Path:'Snippets/Components/NetworkDiscoverySnippet.cs' Name:'discovery-messages' }}} The custom NetworkDiscovery class contains the overrides for handling the messages above. You may want to refer to the NetworkDiscovery.cs script in the Components/Discovery folder to see how these should be implemented. -```cs -public class NewNetworkDiscovery: NetworkDiscoveryBase -{ - #region Server - - protected override void ProcessClientRequest(DiscoveryRequest request, IPEndPoint endpoint) - { - base.ProcessClientRequest(request, endpoint); - } - - protected override DiscoveryResponse ProcessRequest(DiscoveryRequest request, IPEndPoint endpoint) - { - // TODO: Create your response and return it - return new DiscoveryResponse(); - } - - #endregion - - #region Client - - protected override DiscoveryRequest GetRequest() - { - return new DiscoveryRequest(); - } - - protected override void ProcessResponse(DiscoveryResponse response, IPEndPoint endpoint) - { - // TODO: a server replied, do something with the response such as invoking a unityevent - } - - #endregion -} -``` +{{{ Path:'Snippets/Components/NetworkDiscoverySnippet.cs' Name:'discovery-custom' }}} diff --git a/doc/docs/components/network-log-settings.md b/doc/docs/components/network-log-settings.md index 0f17cdd0a8c..a82d713d827 100644 --- a/doc/docs/components/network-log-settings.md +++ b/doc/docs/components/network-log-settings.md @@ -60,27 +60,7 @@ You can set `MirageLogHandler` as the default log handler for all Mirage loggers Here's an example of how to set `MirageLogHandler` with default settings: -```csharp -using Mirage.Logging; -using UnityEngine; - -public class CustomLogSetup : MonoBehaviour -{ - void Awake() - { - // Create default settings for MirageLogHandler - var settings = new MirageLogHandler.Settings( - timePrefix: MirageLogHandler.TimePrefix.DateTimeMilliSeconds, - coloredLabel: true, - label: true - ); - - // Replace the default log handler with MirageLogHandler - // This will apply to all existing and future loggers - LogFactory.ReplaceLogHandler((loggerName) => new MirageLogHandler(settings, loggerName)); - } -} -``` +{{{ Path:'Snippets/Components/NetworkLogSettingsSnippet.cs' Name:'custom-log-setup' }}} ### `MirageLogHandler` Settings @@ -103,31 +83,7 @@ Mirage's logging system is designed to be easily integrated into your own game c To get an `ILogger` for your class, you typically declare a `static readonly` field at the top of your class. **`LogFactory.GetLogger()` (or `LogFactory.GetLogger("YourCustomLoggerName")`) will always return the same `ILogger` instance for a given logger name, ensuring that any modifications to its settings (e.g., `filterLogType`) will apply consistently across your application.** Here's how: -```csharp -using Mirage.Logging; // Make sure to include this namespace -using UnityEngine; - -public class MyGameManager : MonoBehaviour -{ - // Obtain a logger for this class. - // The LogFactory will automatically manage its log level based on your LogSettingsSO. - private static readonly ILogger logger = LogFactory.GetLogger(); - - void Start() - { - // Example usage of the logger - logger.Log("MyGameManager started!"); - logger.LogWarning("Something might be wrong here."); - logger.LogError("Critical error occurred!"); - - // You can also check if a log type is enabled before logging to avoid unnecessary string formatting - if (logger.LogEnabled()) - { - logger.Log($"Current time: {Time.time}"); - } - } -} -``` +{{{ Path:'Snippets/Components/NetworkLogSettingsSnippet.cs' Name:'my-game-manager' }}} ### Benefits diff --git a/doc/docs/components/network-scene-checker.md b/doc/docs/components/network-scene-checker.md index 6a0d77ec647..c3876432053 100644 --- a/doc/docs/components/network-scene-checker.md +++ b/doc/docs/components/network-scene-checker.md @@ -45,37 +45,15 @@ All character objects are always first spawned in the main scene, which may or m **Loading the sub-scene(s) on the server:** -```cs -SceneManager.LoadSceneAsync(subScene, LoadSceneMode.Additive); -``` +{{{ Path:'Snippets/Components/NetworkSceneCheckerSnippet.cs' Name:'load-scene-async' }}} **Sending a `SceneMessage` to the client to load a sub-scene additively:** -```cs -SceneMessage msg = new SceneMessage -{ - sceneName = subScene, - sceneOperation = SceneOperation.LoadAdditive -}; - -Owner.Send(msg); -``` +{{{ Path:'Snippets/Components/NetworkSceneCheckerSnippet.cs' Name:'send-scene-message' }}} **Moving the character object to the sub-scene using `SceneVisibilityChecker.MoveToScene`:** -```cs -// Get the SceneVisibilityChecker component -SceneVisibilityChecker sceneChecker = player.GetComponent(); -if (sceneChecker != null) -{ - // Position the character object in world space first (if needed) - // This assumes it has a NetworkTransform component that will update clients - player.transform.position = new Vector3(100, 1, 100); - - // Then move the character object to the subscene using the checker's method - sceneChecker.MoveToScene(subScene); -} -``` +{{{ Path:'Snippets/Components/NetworkSceneCheckerSnippet.cs' Name:'move-player-to-scene' }}} Optionally you can send another `SceneMessage` to the client with `SceneOperation.UnloadAdditive` to remove any previous additive scene the client no longer needs. This would apply to a game that has levels after a level change. A short delay may be necessary before removal to allow the client to get fully synced. diff --git a/doc/docs/components/network-scene-manager.md b/doc/docs/components/network-scene-manager.md index f9bfc164d8e..4a0817f1709 100644 --- a/doc/docs/components/network-scene-manager.md +++ b/doc/docs/components/network-scene-manager.md @@ -38,16 +38,7 @@ If the scene change involves network objects then it is strongly recommended to To do a network scene change you initiate the process via the server NetworkSceneManager via: -```cs -// For normal scene changes -sceneManager.ServerLoadSceneNormal("Assets/GameScene.unity"); - -// For additive scene loading -sceneManager.ServerLoadSceneAdditively("Assets/AdditiveScene.unity", players); - -// For additive scene unloading -sceneManager.ServerUnloadSceneAdditively(scene, players); -``` +{{{ Path:'Snippets/Components/NetworkSceneManagerSnippet.cs' Name:'scene-change-examples' }}} :::note You don't have to provide the full scene path when initiating a scene change. But the 'NetworkSceneName' will be saved as the full path. diff --git a/doc/docs/components/ready-check.md b/doc/docs/components/ready-check.md index 343a0c99ed4..8edb84c7d96 100644 --- a/doc/docs/components/ready-check.md +++ b/doc/docs/components/ready-check.md @@ -21,25 +21,25 @@ See the API reference for more details To set a player as ready, you can simply update the `IsReady` field of their `ReadyCheck` component to true. This can be done either manually through code, or through user input such as a "Ready" button. Mirage will then sync this change to server and other clients. For example: -{{{ Path:'Snippets/LobbyReadyCheck.cs' Name:'set-ready' }}} +{{{ Path:'Snippets/Components/LobbyReadyCheck.cs' Name:'set-ready' }}} #### Reacting to Ready changes When the `IsReady` field of a player's `ReadyCheck` component is changed, the `OnReadyChanged` event is invoked on all clients to reflect the new value. You can subscribe to this event and perform actions based on the player's ready state. For example, you can update UI elements to show the player's current ready status: -{{{ Path:'Snippets/LobbyReadyCheck.cs' Name:'ready-ui' }}} +{{{ Path:'Snippets/Components/LobbyReadyCheck.cs' Name:'ready-ui' }}} #### Sending Messages to Ready Players To send a message to all players that are ready, you can use the `LobbyReady.SendToReady` function. Here's an example: -{{{ Path:'Snippets/LobbyReadyCheck.cs' Name:'send-to-ready' }}} +{{{ Path:'Snippets/Components/LobbyReadyCheck.cs' Name:'send-to-ready' }}} You can also send messages to not ready players by setting the `sendToReady` parameter to false. Note that this function only sends messages to players that have `ReadyCheck` attached to their character and are synced with the server. -{{{ Path:'Snippets/LobbyReadyCheck.cs' Name:'send-to-not-ready' }}} +{{{ Path:'Snippets/Components/LobbyReadyCheck.cs' Name:'send-to-not-ready' }}} #### Resetting Ready @@ -47,6 +47,6 @@ You can also send messages to not ready players by setting the `sendToReady` par Resetting Ready State for All Players You can reset the `IsReady` field for all players by calling `LobbyReady.SetAllClientsNotReady()`. Here's an example: -{{{ Path:'Snippets/LobbyReadyCheck.cs' Name:'set-all-not-ready' }}} +{{{ Path:'Snippets/Components/LobbyReadyCheck.cs' Name:'set-all-not-ready' }}} This will set the `IsReady` field to `false` for all `ReadyCheck` on the server, the values will then be synced to client. diff --git a/doc/docs/general/getting-started.md b/doc/docs/general/getting-started.md index aad4bbda52e..7c28b798b67 100644 --- a/doc/docs/general/getting-started.md +++ b/doc/docs/general/getting-started.md @@ -72,55 +72,11 @@ If you require a camera to run on player prefab subscribe to `Identity.OnStartLo For example, if client authority has been checked and you trust clients. Never trust clients though. -```cs -using UnityEngine; -using Mirage; - -public class Controls : NetworkBehaviour -{ - void Update() - { - if (!IsLocalPlayer) - { - // exit from update if this is not the local player - return; - } - - // handle player input for movement - } -} -``` +{{{ Path:'Snippets/General/GettingStartedSnippets.cs' Name:'getting-started-client-authority' }}} For example, if server authority is going to be used. -```cs -using UnityEngine; -using Mirage; - -public class Controls : NetworkBehaviour -{ - void Update() - { - if (!IsLocalPlayer) - { - // exit from update if this is not the local player - return; - } - - // handle player input for movement - - // You would call this command after handling input or you can send inputs directly to - // server and let server buffer inputs up and do movements based on the buffered inputs. - MovePlayer(); - } - - [ServerRpc] - void MovePlayer() - { - // We are now firing off some kind of movement all done by server. - } -} -``` +{{{ Path:'Snippets/General/GettingStartedSnippets.cs' Name:'getting-started-server-authority' }}} ## Basic Player Game State - Make scripts that contain important data into NetworkBehaviours instead of MonoBehaviours diff --git a/doc/docs/general/troubleshooting.md b/doc/docs/general/troubleshooting.md index 5a091373a26..9c58a64299c 100644 --- a/doc/docs/general/troubleshooting.md +++ b/doc/docs/general/troubleshooting.md @@ -16,27 +16,9 @@ If it does not find one, it assumes you are not trying to serialize the type so For example, you might get this error with this code when trying to sync the [SyncList](/docs/guides/sync/sync-objects/sync-list). -```cs -public struct MyCustomType -{ - public int id; - public string name; -} - -class MyBehaviour : NetworkBehaviour -{ - private readonly SyncList myList = new SyncList(); -} -``` +{{{ Path:'Snippets/General/TroubleshootingSnippets.cs' Name:'troubleshooting-no-writer-triggering' }}} In this case, there is no direct invocation to send or receive. So Mirage does not know about it. **There is a simple workaround:** add an `[NetworkMessage]` attribute to your class or struct. -```cs -[NetworkMessage] // Added attribute -public struct MyCustomType -{ - public int id; - public string name; -} -``` +{{{ Path:'Snippets/General/TroubleshootingSnippets.cs' Name:'troubleshooting-no-writer-resolved' }}} diff --git a/doc/docs/guides/attributes.md b/doc/docs/guides/attributes.md index 85c15b38734..c3290b428cf 100644 --- a/doc/docs/guides/attributes.md +++ b/doc/docs/guides/attributes.md @@ -53,22 +53,9 @@ These attributes can be used for Unity game loop methods like `Start`, `Update` #### Examples: -```cs -[Server] -void SpawnCoin() -{ - // This method is only allowed to be invoked on the server. -} -``` - -```cs -[NetworkMethod(NetworkFlags.Server | NetworkFlags.NotActive)] -public void StartGame() -{ - // This method will run on the server or in single-player mode. - // It will only be blocked if the client is active. -} -``` +{{{ Path:'Snippets/General/AttributesSnippets.cs' Name:'attributes-server' }}} + +{{{ Path:'Snippets/General/AttributesSnippets.cs' Name:'attributes-network-method' }}} ## Max Length Attribute diff --git a/doc/docs/guides/authentication/basic-authenticator.md b/doc/docs/guides/authentication/basic-authenticator.md index cd7667f529e..cfbfd779e29 100644 --- a/doc/docs/guides/authentication/basic-authenticator.md +++ b/doc/docs/guides/authentication/basic-authenticator.md @@ -13,8 +13,6 @@ After performing these steps, the inspector should look like this: You can authenticate with the server by calling the `SendCode` method provided by the authenticator. This method allows you to send a server code to the server for authentication. -```csharp -public void SendCode(NetworkClient client, string serverCode = null) -``` +{{{ Path:'Snippets/Authentication/BasicAuthenticatorSnippets.cs' Name:'basic-authenticator-sendcode' }}} If the `serverCode` parameter is `null`, the method will use the value specified in the public `ServerCode` field of the Basic Authenticator component. diff --git a/doc/docs/guides/authentication/custom-authenticator.md b/doc/docs/guides/authentication/custom-authenticator.md index 2dddd93d55a..bdc6f8320b1 100644 --- a/doc/docs/guides/authentication/custom-authenticator.md +++ b/doc/docs/guides/authentication/custom-authenticator.md @@ -14,10 +14,10 @@ To create a custom Authenticator, follow these steps: **Step 1: Inherit from `NetworkAuthenticatorBase`** -{{{ Path:'Snippets/CustomAuthenticator.cs' Name:'authenticator-def' }}} +{{{ Path:'Snippets/Authentication/CustomAuthenticator.cs' Name:'authenticator-def' }}} **Step 2: Create a Network Message** -{{{ Path:'Snippets/CustomAuthenticator.cs' Name:'auth-message' }}} +{{{ Path:'Snippets/Authentication/CustomAuthenticator.cs' Name:'auth-message' }}} Clients should use the `SendAuthentication(NetworkClient client, T msg)` method to correctly send the authentication message. @@ -26,10 +26,10 @@ Using `player.Send` directly will not work because the authenticator message is ::: **Step 3: Implement the Authenticator** -{{{ Path:'Snippets/CustomAuthenticator.cs' Name:'authenticator' }}} +{{{ Path:'Snippets/Authentication/CustomAuthenticator.cs' Name:'authenticator' }}} **Step 4: Return Additional Data (Optional)** -{{{ Path:'Snippets/CustomAuthenticator.cs' Name:'auth-data' }}} +{{{ Path:'Snippets/Authentication/CustomAuthenticator.cs' Name:'auth-data' }}} **Step 5: Retrieve Custom Data** -{{{ Path:'Snippets/CustomAuthenticator.cs' Name:'use-data' }}} +{{{ Path:'Snippets/Authentication/CustomAuthenticator.cs' Name:'use-data' }}} diff --git a/doc/docs/guides/authority.md b/doc/docs/guides/authority.md index ec7c6e96ee1..3f58e2cc15e 100644 --- a/doc/docs/guides/authority.md +++ b/doc/docs/guides/authority.md @@ -27,36 +27,22 @@ If you spawn a character object using `ServerObjectManager.AddCharacter` then it ### Using NetworkServer.Spawn You can give authority to a client when an object is spawned. This is done by passing in the connection to the spawn message -```cs -GameObject go = Instantiate(prefab); -ServerObjectManager.Spawn(go, owner); -``` +{{{ Path:'Snippets/General/AuthoritySnippets.cs' Name:'authority-spawn-with-owner' }}} ### Using identity.AssignClientAuthority You can give authority to a client at any time using `AssignClientAuthority`. This can be done by calling `AssignClientAuthority` on the object you want to give authority too -```cs -Identity.AssignClientAuthority(conn); -``` +{{{ Path:'Snippets/General/AuthoritySnippets.cs' Name:'authority-assign-client-authority' }}} You may want to do this when a player picks up an item -```cs -// Command on character object -[ServerRpc] -void PickupItem(NetworkIdentity item) -{ - item.AssignClientAuthority(connectionToClient); -} -``` +{{{ Path:'Snippets/General/AuthoritySnippets.cs' Name:'authority-pickup-item' }}} ## How to remove authority You can use `Identity.RemoveClientAuthority` to remove client authority from an object. -```cs -Identity.RemoveClientAuthority(); -``` +{{{ Path:'Snippets/General/AuthoritySnippets.cs' Name:'authority-remove-client-authority' }}} Authority can't be removed from the character object. Instead, you will have to replace the character object using `NetworkServer.ReplaceCharacter`. diff --git a/doc/docs/guides/best-practices.md b/doc/docs/guides/best-practices.md index d6e2b57b21c..7606333028f 100644 --- a/doc/docs/guides/best-practices.md +++ b/doc/docs/guides/best-practices.md @@ -11,10 +11,4 @@ This page is a work in progress If you send custom message regularly then the message should be a struct so that there is no GC/allocations. -```cs -struct CreateVisualEffect -{ - public Vector3 position; - public Guid prefabId; -} -``` +{{{ Path:'Snippets/General/BestPracticesSnippets.cs' Name:'best-practices-custom-message' }}} diff --git a/doc/docs/guides/bit-packing/bit-count-from-range.md b/doc/docs/guides/bit-packing/bit-count-from-range.md index ab5317b70cb..ee485b9c4ee 100644 --- a/doc/docs/guides/bit-packing/bit-count-from-range.md +++ b/doc/docs/guides/bit-packing/bit-count-from-range.md @@ -29,13 +29,7 @@ Values are written using `Write(value - min, bitCount)` and read using `value = A modifier that can add to a character value to increase or decrease it -```cs -public class MyNetworkBehaviour : NetworkBehaviour -{ - [SyncVar, BitCountFromRange(-100, 100)] - public int modifier { get; set; } -} -``` +{{{ Path:'Snippets/BitPacking/BitCountFromRangeSnippets.cs' Name:'bit-count-from-range-example-1' }}} `Range = 200` so bit count is 8, causing the real range to be -100 to 155 @@ -54,19 +48,7 @@ public class MyNetworkBehaviour : NetworkBehaviour A Direction enum to say which way a model is facing -```cs -public enum MyDirection -{ - Backwards = -1, - None = 0, - Forwards = 1, -} -public class MyNetworkBehaviour : NetworkBehaviour -{ - [SyncVar, BitCount(-1, 1)] - public MyDirection direction { get; set; } -} -``` +{{{ Path:'Snippets/BitPacking/BitCountFromRangeSnippets.cs' Name:'bit-count-from-range-example-2' }}} `Range = 3` so bit count is `2`, causing the real range to be -1 to 2 @@ -78,50 +60,9 @@ public class MyNetworkBehaviour : NetworkBehaviour ### Generated Code Source: -```cs -[SyncVar, BitCountFromRange(-100, 100)] -public int myValue { get; set; } -``` +{{{ Path:'Snippets/BitPacking/BitCountFromRangeSnippets.cs' Name:'bit-count-from-range-generated-source' }}} Generated: -```cs -public override bool SerializeSyncVars(NetworkWriter writer, bool initialState) -{ - ulong syncVarDirtyBits = base.SyncVarDirtyBits; - bool result = base.SerializeSyncVars(writer, initialize); - - if (initialState) - { - writer.Write((ulong)(this.myValue - (-100)), 8); - return true; - } - - writer.Write(syncVarDirtyBits, 1); - if ((syncVarDirtyBits & 1UL) != 0UL) - { - writer.Write((ulong)(this.myValue - (-100)), 8); - result = true; - } - - return result; -} - -public override void DeserializeSyncVars(NetworkReader reader, bool initialState) -{ - base.DeserializeSyncVars(reader, initialState); - - if (initialState) - { - this.myValue = reader.Read(8) + (-100); - return; - } - - ulong dirtyMask = reader.Read(1); - if ((dirtyMask & 1UL) != 0UL) - { - this.myValue = reader.Read(8) + (-100); - } -} -``` +{{{ Path:'Snippets/BitPacking/BitCountFromRangeSnippets.cs' Name:'bit-count-from-range-generated-code' }}} *last updated for Mirage v101.8.0* diff --git a/doc/docs/guides/bit-packing/bit-count.md b/doc/docs/guides/bit-packing/bit-count.md index c8c2eb9831d..2685ffff889 100644 --- a/doc/docs/guides/bit-packing/bit-count.md +++ b/doc/docs/guides/bit-packing/bit-count.md @@ -28,13 +28,7 @@ This means that `BitCount` should not be used with values that can be negative b Health which is between 0 and 100 -```cs -public class MyNetworkBehaviour : NetworkBehaviour -{ - [SyncVar, BitCount(7)] - public int Health { get; set; } -} -``` +{{{ Path:'Snippets/BitPacking/BitCountSnippets.cs' Name:'bit-count-example-1' }}} `BitCount = 7` so max value of Health is `127` @@ -48,13 +42,7 @@ public class MyNetworkBehaviour : NetworkBehaviour ### Example 2 Weapon index in a list of 6 weapons -```cs -public class MyNetworkBehaviour : NetworkBehaviour -{ - [SyncVar, BitCount(3)] - public int WeaponIndex { get; set; } -} -``` +{{{ Path:'Snippets/BitPacking/BitCountSnippets.cs' Name:'bit-count-example-2' }}} `BitCount = 3` so max value of Health is 7 @@ -64,50 +52,9 @@ public class MyNetworkBehaviour : NetworkBehaviour ### Generated Code Source: -```cs -[SyncVar, BitCount(7)] -public int myValue { get; set; } -``` +{{{ Path:'Snippets/BitPacking/BitCountSnippets.cs' Name:'bit-count-generated-source' }}} Generated: -```cs -public override bool SerializeSyncVars(NetworkWriter writer, bool initialState) -{ - ulong syncVarDirtyBits = base.SyncVarDirtyBits; - bool result = base.SerializeSyncVars(writer, initialize); - - if (initialState) - { - writer.Write((ulong)this.myValue, 7); - return true; - } - - writer.Write(syncVarDirtyBits, 1); - if ((syncVarDirtyBits & 1UL) != 0UL) - { - writer.Write((ulong)this.myValue, 7); - result = true; - } - - return result; -} - -public override void DeserializeSyncVars(NetworkReader reader, bool initialState) -{ - base.DeserializeSyncVars(reader, initialState); - - if (initialState) - { - this.myValue = reader.Read(7); - return; - } - - ulong dirtyMask = reader.Read(1); - if ((dirtyMask & 1UL) != 0UL) - { - this.myValue = reader.Read(7); - } -} -``` +{{{ Path:'Snippets/BitPacking/BitCountSnippets.cs' Name:'bit-count-generated-code' }}} *Last updated for Mirage v101.8.0.* \ No newline at end of file diff --git a/doc/docs/guides/bit-packing/float-pack.md b/doc/docs/guides/bit-packing/float-pack.md index e0bb82fe220..9e889d3b435 100644 --- a/doc/docs/guides/bit-packing/float-pack.md +++ b/doc/docs/guides/bit-packing/float-pack.md @@ -22,13 +22,7 @@ Values are clamped so values out of range will be packed as min/max values inste Health which is between 0 and 100 -```cs -public class MyNetworkBehaviour : NetworkBehaviour -{ - [SyncVar, FloatPack(100f, 0.02f)] - public int Health { get; set; } -} -``` +{{{ Path:'Snippets/BitPacking/FloatPackSnippets.cs' Name:'float-pack-example-1' }}} `Max = 100`, `resolution = 0.02f` so bit count is 14 @@ -43,66 +37,16 @@ public class MyNetworkBehaviour : NetworkBehaviour A Percent that where you only want to send 8 bits -```cs -public class MyNetworkBehaviour : NetworkBehaviour -{ - [SyncVar, FloatPack(1f, 8)] - public int Percent { get; set; } -} -``` +{{{ Path:'Snippets/BitPacking/FloatPackSnippets.cs' Name:'float-pack-example-2' }}} `Max = 1f`, `bitCount = 8` so resolution will be `0.00787f` ### Generated Code Source: -```cs -[SyncVar, FloatPack(100f, 0.02f)] -public int myValue { get; set; } -``` +{{{ Path:'Snippets/BitPacking/FloatPackSnippets.cs' Name:'float-pack-generated-source' }}} Generated: -```cs - -private FloatPacker myValue__Packer = new FloatPacker(100f, 0.02f); - -public override bool SerializeSyncVars(NetworkWriter writer, bool initialState) -{ - ulong syncVarDirtyBits = base.SyncVarDirtyBits; - bool result = base.SerializeSyncVars(writer, initialize); - - if (initialState) - { - myValue__Packer.Pack(writer, this.myValue); - return true; - } - - writer.Write(syncVarDirtyBits, 1); - if ((syncVarDirtyBits & 1UL) != 0UL) - { - myValue__Packer.Pack(writer, this.myValue); - result = true; - } - - return result; -} - -public override void DeserializeSyncVars(NetworkReader reader, bool initialState) -{ - base.DeserializeSyncVars(reader, initialState); - - if (initialState) - { - this.myValue = myValue__Packer.Unpack(reader); - return; - } - - ulong dirtyMask = reader.Read(1); - if ((dirtyMask & 1UL) != 0UL) - { - this.myValue = myValue__Packer.Unpack(reader); - } -} -``` +{{{ Path:'Snippets/BitPacking/FloatPackSnippets.cs' Name:'float-pack-generated-code' }}} *last updated for Mirage v101.8.0* \ No newline at end of file diff --git a/doc/docs/guides/bit-packing/quaternion-pack.md b/doc/docs/guides/bit-packing/quaternion-pack.md index 5191fade0f6..9213b484db7 100644 --- a/doc/docs/guides/bit-packing/quaternion-pack.md +++ b/doc/docs/guides/bit-packing/quaternion-pack.md @@ -52,64 +52,14 @@ The precision of the smallest 3 can in increased or decreased to change the bit ### Example 1 -```cs -public class MyNetworkBehaviour : NetworkBehaviour -{ - [SyncVar, QuaternionPack(9)] - public Quaternion direction { get; set; } -} -``` +{{{ Path:'Snippets/BitPacking/QuaternionPackSnippets.cs' Name:'quaternion-pack-example-1' }}} ### Generated Code Source: -```cs -[SyncVar, QuaternionPack(9)] -public int myValue { get; set; } -``` +{{{ Path:'Snippets/BitPacking/QuaternionPackSnippets.cs' Name:'quaternion-pack-generated-source' }}} Generated: -```cs - -private QuaternionPacker myValue__Packer = new QuaternionPacker(9); - -public override bool SerializeSyncVars(NetworkWriter writer, bool initialState) -{ - ulong syncVarDirtyBits = base.SyncVarDirtyBits; - bool result = base.SerializeSyncVars(writer, initialize); - - if (initialState) - { - myValue__Packer.Pack(writer, this.myValue); - return true; - } - - writer.Write(syncVarDirtyBits, 1); - if ((syncVarDirtyBits & 1UL) != 0UL) - { - myValue__Packer.Pack(writer, this.myValue); - result = true; - } - - return result; -} - -public override void DeserializeSyncVars(NetworkReader reader, bool initialState) -{ - base.DeserializeSyncVars(reader, initialState); - - if (initialState) - { - this.myValue = myValue__Packer.Unpack(reader); - return; - } - - ulong dirtyMask = reader.Read(1); - if ((dirtyMask & 1UL) != 0UL) - { - this.myValue = myValue__Packer.Unpack(reader); - } -} -``` +{{{ Path:'Snippets/BitPacking/QuaternionPackSnippets.cs' Name:'quaternion-pack-generated-code' }}} *last updated for Mirage v101.8.0* \ No newline at end of file diff --git a/doc/docs/guides/bit-packing/var-int-blocks.md b/doc/docs/guides/bit-packing/var-int-blocks.md index 7466ebcad05..b14a716084c 100644 --- a/doc/docs/guides/bit-packing/var-int-blocks.md +++ b/doc/docs/guides/bit-packing/var-int-blocks.md @@ -21,13 +21,7 @@ Packs an integer value based on its size A modifier that can be added to a character value to increase or decrease it -```cs -public class MyNetworkBehaviour : NetworkBehaviour -{ - [SyncVar, VarIntBlocks(-100, 100)] - public int modifier { get; set; } -} -``` +{{{ Path:'Snippets/BitPacking/VarIntBlocksSnippets.cs' Name:'var-int-blocks-example-1' }}} `Range = 200` so bit count is 8, causing the real range to be -100 to 155 @@ -46,19 +40,7 @@ public class MyNetworkBehaviour : NetworkBehaviour A Direction enum to say which way a model is facing -```cs -public enum MyDirection -{ - Backwards = -1, - None = 0, - Forwards = 1, -} -public class MyNetworkBehaviour : NetworkBehaviour -{ - [SyncVar, BitCount(-1, 1)] - public MyDirection direction { get; set; } -} -``` +{{{ Path:'Snippets/BitPacking/VarIntBlocksSnippets.cs' Name:'var-int-blocks-example-2' }}} `Range = 3` so bit count is `2`, causing the real range to be -1 to 2 @@ -70,50 +52,9 @@ public class MyNetworkBehaviour : NetworkBehaviour ### Generated Code Source: -```cs -[SyncVar, BitCountFromRange(-100, 100)] -public int myValue { get; set; } -``` +{{{ Path:'Snippets/BitPacking/VarIntBlocksSnippets.cs' Name:'var-int-blocks-generated-source' }}} Generated: -```cs -public override bool SerializeSyncVars(NetworkWriter writer, bool initialState) -{ - ulong syncVarDirtyBits = base.SyncVarDirtyBits; - bool result = base.SerializeSyncVars(writer, initialize); - - if (initialState) - { - writer.Write((ulong)(this.myValue - (-100)), 8); - return true; - } - - writer.Write(syncVarDirtyBits, 1); - if ((syncVarDirtyBits & 1UL) != 0UL) - { - writer.Write((ulong)(this.myValue - (-100)), 8); - result = true; - } - - return result; -} - -public override void DeserializeSyncVars(NetworkReader reader, bool initialState) -{ - base.DeserializeSyncVars(reader, initialState); - - if (initialState) - { - this.myValue = reader.Read(8) + (-100); - return; - } - - ulong dirtyMask = reader.Read(1); - if ((dirtyMask & 1UL) != 0UL) - { - this.myValue = reader.Read(8) + (-100); - } -} -``` +{{{ Path:'Snippets/BitPacking/VarIntBlocksSnippets.cs' Name:'var-int-blocks-generated-code' }}} *last updated for Mirage v101.8.0* \ No newline at end of file diff --git a/doc/docs/guides/bit-packing/vector-pack.md b/doc/docs/guides/bit-packing/vector-pack.md index f8dde0eeee7..a6f71ad6af0 100644 --- a/doc/docs/guides/bit-packing/vector-pack.md +++ b/doc/docs/guides/bit-packing/vector-pack.md @@ -17,103 +17,26 @@ These attributes work in the same way as [FloatPack](/docs/guides/bit-packing/fl A Position in bounds +-100 in all XYZ with 0.05 precision for all axis -```cs -public class MyNetworkBehaviour : NetworkBehaviour -{ - [SyncVar, Vector3Pack(100f, 100f, 100f, 0.05f)] - public Vector3 Position { get; set; } -} -``` +{{{ Path:'Snippets/BitPacking/VectorPackSnippets.cs' Name:'vector-pack-example-1' }}} ### Example 2 A Position in bounds +-100 in all XZ with 0.05 precision, but with +-20 and precision 0.1 in y-axis -```cs -public class MyNetworkBehaviour : NetworkBehaviour -{ - [SyncVar, Vector3Pack(100f, 20f, 100f, 0.05f, 0.1f, 0.05f)] - public Vector3 Position { get; set; } -} -``` +{{{ Path:'Snippets/BitPacking/VectorPackSnippets.cs' Name:'vector-pack-example-2' }}} ### Example 3 A position in a 2D map -```cs -public class MyNetworkBehaviour : NetworkBehaviour -{ - [SyncVar, Vector2Pack(1000f, 80f, 0.05f)] - public Vector2 Position { get; set; } -} -``` +{{{ Path:'Snippets/BitPacking/VectorPackSnippets.cs' Name:'vector-pack-example-3' }}} ### Generated Code Source: -```cs -[SyncVar, Vector3Pack(100f, 20f, 100f, 0.05f, 0.1f, 0.05f)] -public int myValue1 { get; set; } - -[SyncVar, Vector2Pack(1000f, 80f, 0.05f)] -public int myValue2 { get; set; } -``` +{{{ Path:'Snippets/BitPacking/VectorPackSnippets.cs' Name:'vector-pack-generated-source' }}} Generated: -```cs - -private Vector3Packer myValue1__Packer = new Vector3Packer(1100f, 20f, 100f, 0.05f, 0.1f, 0.05f); -private Vector2Packer myValue2__Packer = new Vector2Packer(1000f, 80f, 0.05f, 0.05f); - -public override bool SerializeSyncVars(NetworkWriter writer, bool initialState) -{ - ulong syncVarDirtyBits = base.SyncVarDirtyBits; - bool result = base.SerializeSyncVars(writer, initialize); - - if (initialState) - { - myValue1__Packer.Pack(writer, this.myValue1); - myValue2__Packer.Pack(writer, this.myValue2); - return true; - } - - writer.Write(syncVarDirtyBits, 2); - if ((syncVarDirtyBits & 1UL) != 0UL) - { - myValue1__Packer.Pack(writer, this.myValue1); - result = true; - } - if ((syncVarDirtyBits & 2UL) != 0UL) - { - myValue2__Packer.Pack(writer, this.myValue2); - result = true; - } - - return result; -} - -public override void DeserializeSyncVars(NetworkReader reader, bool initialState) -{ - base.DeserializeSyncVars(reader, initialState); - - if (initialState) - { - this.myValue1 = myValue1__Packer.Unpack(reader); - this.myValue2 = myValue2__Packer.Unpack(reader); - return; - } - - ulong dirtyMask = reader.Read(2); - if ((dirtyMask & 1UL) != 0UL) - { - this.myValue1 = myValue1__Packer.Unpack(reader); - } - if ((dirtyMask & 2UL) != 0UL) - { - this.myValue2 = myValue2__Packer.Unpack(reader); - } -} -``` +{{{ Path:'Snippets/BitPacking/VectorPackSnippets.cs' Name:'vector-pack-generated-code' }}} *last updated for Mirage v101.8.0* \ No newline at end of file diff --git a/doc/docs/guides/bit-packing/zig-zag-encode.md b/doc/docs/guides/bit-packing/zig-zag-encode.md index e6998d57a73..c957a5ffbb0 100644 --- a/doc/docs/guides/bit-packing/zig-zag-encode.md +++ b/doc/docs/guides/bit-packing/zig-zag-encode.md @@ -28,13 +28,7 @@ The sign of a value will take up 1 bit, so if the value is in the range -+100 it A modifier that can be added to a character value to increase or decrease it -```cs -public class MyNetworkBehaviour : NetworkBehaviour -{ - [SyncVar, BitCount(8), ZigZagEncode] - public int modifier { get; set; } -} -``` +{{{ Path:'Snippets/BitPacking/ZigZagEncodeSnippets.cs' Name:'zig-zag-encode-example-1' }}} `Range = 200` so bit count is 8, causing the real range to be -128 to 127 @@ -49,50 +43,9 @@ public class MyNetworkBehaviour : NetworkBehaviour ### Generated Code Source: -```cs -[SyncVar, BitCount(8), ZigZagEncode] -public int myValue { get; set; } -``` +{{{ Path:'Snippets/BitPacking/ZigZagEncodeSnippets.cs' Name:'zig-zag-encode-generated-source' }}} Generated: -```cs -public override bool SerializeSyncVars(NetworkWriter writer, bool initialState) -{ - ulong syncVarDirtyBits = base.SyncVarDirtyBits; - bool result = base.SerializeSyncVars(writer, initialize); - - if (initialState) - { - writer.Write((ulong)ZigZag.Encode(this.myValue), 8); - return true; - } - - writer.Write(syncVarDirtyBits, 1); - if ((syncVarDirtyBits & 1UL) != 0UL) - { - writer.Write((ulong)ZigZag.Encode(this.myValue), 8); - result = true; - } - - return result; -} - -public override void DeserializeSyncVars(NetworkReader reader, bool initialState) -{ - base.DeserializeSyncVars(reader, initialState); - - if (initialState) - { - this.myValue = ZigZag.Decode(reader.Read(8)); - return; - } - - ulong dirtyMask = reader.Read(1); - if ((dirtyMask & 1UL) != 0UL) - { - this.myValue = ZigZag.Decode(reader.Read(8)); - } -} -``` +{{{ Path:'Snippets/BitPacking/ZigZagEncodeSnippets.cs' Name:'zig-zag-encode-generated-code' }}} *last updated for Mirage v101.8.0* \ No newline at end of file diff --git a/doc/docs/guides/callbacks/network-behaviour.md b/doc/docs/guides/callbacks/network-behaviour.md index baa1e6227ef..e6cf6d6755e 100644 --- a/doc/docs/guides/callbacks/network-behaviour.md +++ b/doc/docs/guides/callbacks/network-behaviour.md @@ -10,29 +10,7 @@ There are a number of events relating to network behaviours that can occur over To use an event you must add a function as a listener, this function will then be called when the event occurs. Some events, like `OnStartServer`, will call the listener immediately if the event was previously called. This allows you to add the listeners at any point without worrying about missing the Invoke. -```cs -void Awake() -{ - Identity.OnStartServer.AddListener(MyStartServer); - Identity.OnStartClient.AddListener(MyStartClient); - Identity.OnStartLocalPlayer.AddListener(MyStartLocalPlayer); -} - -void MyStartServer() -{ - // ... -} - -void MyStartClient() -{ - // ... -} - -void MyStartLocalPlayer() -{ - // ... -} -``` +{{{ Path:'Snippets/Callbacks/NetworkBehaviourCallbacks.cs' Name:'network-behaviour-callbacks' }}} This is a full list of virtual methods (callbacks) that you can implement on `NetworkBehaviour`, and where they are called diff --git a/doc/docs/guides/clock-sync.md b/doc/docs/guides/clock-sync.md index 83d16673bd6..1c6540231c6 100644 --- a/doc/docs/guides/clock-sync.md +++ b/doc/docs/guides/clock-sync.md @@ -6,9 +6,7 @@ sidebar_position: 5 For many features, you need the clock to be synchronized between the client and the server. Mirage does that automatically for you. To get the current time use this code: -```cs -double now = NetworkTime.Time; -``` +{{{ Path:'Snippets/General/ClockSyncSnippets.cs' Name:'clock-sync-time' }}} It will return the same value on the client and the server. It starts at 0 when the server starts. @@ -23,9 +21,7 @@ The time is a double and should never be cast to a float. Casting this down to a Mirage will also calculate the **Return Trip Time** as seen by the application: -```cs -double rtt = NetworkTime.Rtt; -``` +{{{ Path:'Snippets/General/ClockSyncSnippets.cs' Name:'clock-sync-rtt' }}} :::note Return RTT will also be affected by the frame rate. A higher frame rate will mean less delay before the server reads the ping message and replies. @@ -33,23 +29,17 @@ Return RTT will also be affected by the frame rate. A higher frame rate will mea You can check the precision using: -```cs -double timeStandardDeviation = NetworkTime.TimeSd; -``` +{{{ Path:'Snippets/General/ClockSyncSnippets.cs' Name:'clock-sync-time-sd' }}} For example, if this returns 0.2, it means the time measurements swing up and down roughly 0.2 seconds. Network time is smoothing out the values using [Exponential moving average](https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average). You can configure how often you want the client to send pings using: -```cs -NetworkTime.PingInterval = 2.0f; -``` +{{{ Path:'Snippets/General/ClockSyncSnippets.cs' Name:'clock-sync-ping-interval' }}} You can configure how quickly results will change using: -```cs -NetworkTime.PingWindowSize = 10; -``` +{{{ Path:'Snippets/General/ClockSyncSnippets.cs' Name:'clock-sync-ping-window-size' }}} A higher number will result in smoother results, but a longer time to adjust to changes. diff --git a/doc/docs/guides/community-guides/mirage-quick-start-guide.md b/doc/docs/guides/community-guides/mirage-quick-start-guide.md index 15af4a69382..79a07f5e7e4 100644 --- a/doc/docs/guides/community-guides/mirage-quick-start-guide.md +++ b/doc/docs/guides/community-guides/mirage-quick-start-guide.md @@ -96,37 +96,7 @@ So this is very easy, just go to your `NetworkManager` GO and open (if it is not The last step we will need to do is simple: go to the script we created before (you can go into the assets folder and it will be there) and double click it and it will open your IDE. So what we will need to do is simple: tell how we are moving the user, and also to set the camera as a child of the player. We can do that simply like this: -```cs -using Mirage; -using UnityEngine; - -namespace GettingStarted -{ - public class PlayerScript : NetworkBehaviour - { - private void Awake() { - Identity.OnStartLocalPlayer.AddListener(OnStartLocalPlayer); - } - - private void OnStartLocalPlayer() - { - Camera.main.transform.SetParent(transform); - Camera.main.transform.localPosition = new Vector3(0, 0, 0); - } - - private void Update() - { - if (!IsLocalPlayer) { return; } - - float moveX = Input.GetAxis("Horizontal") * Time.deltaTime * 110.0f; - float moveZ = Input.GetAxis("Vertical") * Time.deltaTime * 4f; - - transform.Rotate(0, moveX, 0); - transform.Translate(0, 0, moveZ); - } - } -} -``` +{{{ Path:'Snippets/CommunityGuides/QuickStartGuide.cs' Name:'quickstart-playerscript-base' }}} Press play in Unity editor and... what happened? Why is our player don't spawning? Well, the question is very simple. You need to start the server somehow, and that's what coming next, but before... @@ -154,25 +124,7 @@ This one is pretty simple, we just need to go to our `NetworkManager` GO then - Create a new script, we can call it `StartServer` - Then server starts should look like this: -```cs -using Mirage; -using UnityEngine; - -namespace GettingStarted -{ - public class StartServer : MonoBehaviour - { - [SerializeField] private NetworkManager networkManager; - - private void Start() - { - if (!networkManager) { return; } - - networkManager.Server.StartServer(networkManager.Client); - } - } -} -``` +{{{ Path:'Snippets/CommunityGuides/QuickStartGuide.cs' Name:'quickstart-startserver' }}} After we save the file, we go back into our `NetworkManager` GO, and assign the NetworkManager field to the script. @@ -197,81 +149,7 @@ Player name above heads ![](/img/guides/community-guides/mirage-quick-start-guide/image--008.jpg) Update your PlayerScript.cs with this: -```cs -using Mirage; -using UnityEngine; - -namespace QuickStart -{ - public class PlayerScript : NetworkBehaviour - { - public TextMesh playerNameText; - public GameObject floatingInfo; - - private Material playerMaterialClone; - - [SyncVar(hook = nameof(OnNameChanged))] - public string playerName { get; set; } - - [SyncVar(hook = nameof(OnColorChanged))] - public Color playerColor { get; set; } = Color.white; - - [ServerRpc] - public void CmdSetupPlayer(string _name, Color _col) - { - // player info sent to server, then server updates sync vars which handles it on all clients - playerName = _name; - playerColor = _col; - } - - private void Awake() { - Identity.OnStartLocalPlayer.AddListener(OnStartLocalPlayer); - } - - private void OnStartLocalPlayer() - { - Camera.main.transform.SetParent(transform); - Camera.main.transform.localPosition = new Vector3(0, 0, 0); - - floatingInfo.transform.localPosition = new Vector3(0, -0.3f, 0.6f); - floatingInfo.transform.localScale = new Vector3(0.1f, 0.1f, 0.1f); - - string name = "Player" + Random.Range(100, 999); - Color color = new Color(Random.Range(0f, 1f), Random.Range(0f, 1f), Random.Range(0f, 1f)) - CmdSetupPlayer(name, color); - } - - private void OnNameChanged(string _Old, string _New) - { - playerNameText.text = playerName; - } - - private void OnColorChanged(Color _Old, Color _New) - { - playerNameText.color = _New; - playerMaterialClone = new Material(GetComponent().material); - playerMaterialClone.color = _New; - GetComponent().material = playerMaterialClone; - } - - private void Update() - { - if (!IsLocalPlayer) - { - // make non-local players run this - floatingInfo.transform.LookAt(Camera.main.transform); - return; - } - - float moveX = Input.GetAxis("Horizontal") * Time.deltaTime * 110.0f; - float moveZ = Input.GetAxis("Vertical") * Time.deltaTime * 4f; - - transform.Rotate(0, moveX, 0); - transform.Translate(0, 0, moveZ); - } - } -} -``` +{{{ Path:'Snippets/CommunityGuides/QuickStartGuide.cs' Name:'quickstart-playerscript-names' }}} Add the `PlayerNameText` and `FloatingInfo` objects into the script on the player prefab, as shown below. @@ -297,70 +175,11 @@ Then create a Canvas with text and a button, similar to the image below. Add the sceneScript variable, Awake function, and CmdSendPlayerMessage to PlayerScript.cs Also add the new playerName joined line to CmdSetupPlayer(); -```cs -private SceneScript sceneScript; - -void Awake() -{ - //allow all players to run this - sceneScript = GameObject.FindObjectOfType(); - Identity.OnStartLocalPlayer.AddListener(OnStartLocalPlayer); -} -[ServerRpc] -public void CmdSendPlayerMessage() -{ - if (sceneScript) - { - sceneScript.statusText = $"{playerName} says hello {Random.Range(10, 99)}"; - } -} -[ServerRpc] -public void CmdSetupPlayer(string _name, Color _col) -{ - //player info sent to server, then server updates sync vars which handles it on all clients - playerName = _name; - playerColor = _col; - sceneScript.statusText = $"{playerName} joined."; -} -public void OnStartLocalPlayer() -{ - sceneScript.playerScript = this; - //. . . . ^ new line to add here -``` +{{{ Path:'Snippets/CommunityGuides/QuickStartGuide.cs' Name:'quickstart-playerscript-part11' }}} Add this code to SceneScript.cs -```cs -using Mirage; -using UnityEngine; -using UnityEngine.UI; - -namespace QuickStart -{ - public class SceneScript : NetworkBehaviour - { - public Text canvasStatusText; - public PlayerScript playerScript; - - [SyncVar(hook = nameof(OnStatusTextChanged))] - public string statusText { get; set; } - - void OnStatusTextChanged(string _Old, string _New) - { - //called from sync var hook, to update info on screen for all players - canvasStatusText.text = statusText; - } - - public void ButtonSendMessage() - { - if (playerScript != null) - { - playerScript.CmdSendPlayerMessage(); - } - } - } -} -``` +{{{ Path:'Snippets/CommunityGuides/QuickStartGuide.cs' Name:'quickstart-scenescript' }}} - Attach the ButtonSendMessage function to your Canvas Button. @@ -387,78 +206,9 @@ Experiment and adjust, have fun! Weapon switching! The code bits. Add the following to your PlayerScript.cs -```cs -private int selectedWeaponLocal = 1; -public GameObject[] weaponArray; - -[SyncVar(hook = nameof(OnWeaponChanged))] -public int activeWeaponSynced { get; set; } - -void OnWeaponChanged(int _Old, int _New) -{ - // disable old weapon - // in range and not null - if (0 < _Old && _Old < weaponArray.Length && weaponArray[_Old] != null) - { - weaponArray[_Old].SetActive(false); - } - - // enable new weapon - // in range and not null - if (0 < _New && _New < weaponArray.Length && weaponArray[_New] != null) - { - weaponArray[_New].SetActive(true); - } -} - -[ServerRpc] -public void CmdChangeActiveWeapon(int newIndex) -{ - activeWeaponSynced = newIndex; -} - -void Awake() -{ - // disable all weapons - foreach (var item in weaponArray) - { - if (item != null) - { - item.SetActive(false); - } - } -} -``` +{{{ Path:'Snippets/CommunityGuides/QuickStartGuide.cs' Name:'quickstart-playerscript-weaponswitch' }}} Add the weapon switch button in `Update`. Only the local player switches its own weapon, so it goes below the `!IsLocalPlayer` check. -```cs -void Update() -{ - if (!IsLocalPlayer) - { - // make non-local players run this - floatingInfo.transform.LookAt(Camera.main.transform); - return; - } - - float moveX = Input.GetAxis("Horizontal") * Time.deltaTime * 110.0f; - float moveZ = Input.GetAxis("Vertical") * Time.deltaTime * 4f; - - transform.Rotate(0, moveX, 0); - transform.Translate(0, 0, moveZ); - - if (Input.GetButtonDown("Fire2")) //Fire2 is mouse 2nd click and left alt - { - selectedWeaponLocal += 1; - - if (selectedWeaponLocal > weaponArray.Length) - { - selectedWeaponLocal = 1; - } - - CmdChangeActiveWeapon(selectedWeaponLocal); - } -} -``` +{{{ Path:'Snippets/CommunityGuides/QuickStartGuide.cs' Name:'quickstart-playerscript-weaponswitch-update' }}} Weapon models diff --git a/doc/docs/guides/error-handling.md b/doc/docs/guides/error-handling.md index 309a89dab79..cf0c053ed1a 100644 --- a/doc/docs/guides/error-handling.md +++ b/doc/docs/guides/error-handling.md @@ -17,45 +17,13 @@ This rate-limiting system is server-side only and replaces the old `DisconnectOn The `PlayerErrorFlags` enum helps categorize the types of errors a player can cause, allowing for more granular tracking and response. -```csharp -// see PlayerErrorFlags in the source code for most up-to-date values -[Flags] -public enum PlayerErrorFlags -{ - None = 0, - - // Likely developer bugs - RpcNullException = 1 << 0, - RpcException = 1 << 1, - - // Connection/versioning issues - DeserializationException = 1 << 2, - RpcSync = 1 << 3, - RateLimit = 1 << 4, - - // Security/Malicious Intent - Unauthorized = 1 << 5, - Critical = 1 << 6, - LikelyCheater = 1 << 7, - SerializationLimit = 1 << 10, - - // Custom developer defined errors - CustomError = 1 << 16 -} -``` +{{{ Path:'Snippets/General/ErrorHandlingSnippets.cs' Name:'error-handling-flags' }}} * **`SerializationLimit`**: Triggered when a client sends a payload (like a string, list, or array) whose size exceeds the limit defined by the `[MaxLength]` attribute, throwing a `SerializationLimitException`. This carries a cost of `100` to quickly penalize and disconnect potentially malicious clients. You can use these flags to identify an error's cause when implementing custom logic. You can also define your own flags using the `CustomError` bit as a starting point: - -```csharp -public static class MyErrorFlags -{ - public const PlayerErrorFlags InvalidTrade = PlayerErrorFlags.CustomError << 0; - public const PlayerErrorFlags AnotherCustom = PlayerErrorFlags.CustomError << 1; -} -``` +{{{ Path:'Snippets/General/ErrorHandlingSnippets.cs' Name:'error-handling-custom-flags' }}} ## Server Configuration @@ -86,86 +54,22 @@ The `cost` parameter specifies how many tokens to subtract from the player's err ### Custom Error Example -```csharp -public static class MyErrorFlags -{ - public const PlayerErrorFlags InvalidAction = PlayerErrorFlags.CustomError << 0; -} +{{{ Path:'Snippets/General/ErrorHandlingSnippets.cs' Name:'error-handling-custom-error-class' }}} // ... inside a NetworkBehaviour -[ServerRpc] -void CmdDoSomething(int data) -{ - // The IsActionValid method would contain your custom validation logic. - if (!IsActionValid(data)) - { - // Penalize the player with a moderate cost for sending invalid data. - Owner.SetError(10, MyErrorFlags.InvalidAction); - return; - } - - // ... process valid data -} -``` +{{{ Path:'Snippets/General/ErrorHandlingSnippets.cs' Name:'error-handling-custom-error-method' }}} ### Critical Error Example For severe violations, use `PlayerErrorFlags.Critical` with a high cost to trigger the handler instantly. -```csharp -[ServerRpc] -void CmdTryAdminAction(string command) -{ - // The IsAdmin method would check if the player has admin privileges. - if (!IsAdmin(Owner)) - { - // A non-admin tried to use an admin command. - // Set cost higher than MaxTokens (default 200) to trigger the limit immediately. - Owner.SetError(10000, PlayerErrorFlags.Critical); - return; - } - - // ... execute admin command -} -``` +{{{ Path:'Snippets/General/ErrorHandlingSnippets.cs' Name:'error-handling-admin-action' }}} ### ServerRpc Without Authority (with Sender) Sometimes you need a `ServerRpc` to be callable from any client, not just the owner of the `NetworkBehaviour`, and you need to know which client sent the RPC. Use `requireAuthority = false` and include `INetworkPlayer sender = null` as a parameter. -```csharp -[Client] -public void SendPublicMessage(string message) -{ - // client side check before sending message - if (string.IsNullOrWhiteSpace(message) || message.Length > 100) - return; - - CmdSendPublicMessage(message) -} - -[ServerRpc(requireAuthority = false)] -void CmdSendPublicMessage(string message, INetworkPlayer sender = null) -{ - if (string.IsNullOrWhiteSpace(message) || message.Length > 100) - { - // Invalid message length. this is very likely a cheat because message length is checked on client before - // how ever this is just chat message nothing not critical gameplay - // for example could be from chat mod with higher size that they left on after playing on a modded server - sender.SetError(50, PlayerErrorFlags.LikelyCheater); - return; - } - - if (CheckMessageRateLimit(sender)) - { - // player sent more message than chat rate limit, just use low cost - sender.SetError(1, PlayerErrorFlags.None); - return; - } - - // ... -} -``` +{{{ Path:'Snippets/General/ErrorHandlingSnippets.cs' Name:'error-handling-public-message' }}} ## Custom Error Handling @@ -175,42 +79,4 @@ This callback is best used alongside `NetworkAuthenticator` so that you can ban You can check `player.ErrorFlags` to see how important the errors have been. -```csharp -using Mirage; -using UnityEngine; - -public class MyGameServer : MonoBehaviour -{ - public NetworkServer server; - - void Start() - { - server.SetErrorRateLimitReachedCallback(OnPlayerErrorLimitReached); - } - - void OnPlayerErrorLimitReached(INetworkPlayer player) - { - Debug.LogWarning($"Player {player} reached error limit with flags: {player.ErrorFlags}"); - - if ((player.ErrorFlags & PlayerErrorFlags.Critical) != 0) - { - // For critical errors, always disconnect. - player.Disconnect(); - - // ... add player to ban or timeout list here so they can't reconnect - - return; - } - else if ((player.ErrorFlags & MyErrorFlags.InvalidAction) != 0) - { - // For our custom action, maybe just send a warning. - // Note: You would need to implement the ChatMessage struct and its handler. - // player.Send(new ChatMessage("You are performing too many invalid actions.")); - } - // ... other custom logic - - // Reset flags after handling - player.ResetErrorFlag(); - } -} -``` +{{{ Path:'Snippets/General/ErrorHandlingSnippets.cs' Name:'error-handling-custom-handler' }}} diff --git a/doc/docs/guides/faq.md b/doc/docs/guides/faq.md index 1cf90be8ba0..c07d11d4762 100644 --- a/doc/docs/guides/faq.md +++ b/doc/docs/guides/faq.md @@ -16,19 +16,7 @@ This page is a work in progress For example, Mirage will automatically create a function for `MyCustomStruct` so that it can be sent without any extra work. - ```cs - [ClientRpc] - public void RpcDoSomething(MyCustomStruct data) - { - // do stuff here - } - - struct MyCustomStruct - { - int someNumber; - Vector3 somePosition; - } - ``` +{{{ Path:'Snippets/Guides/FaqSnippets.cs' Name:'faq-custom-data' }}} For More details - [Data Types](/docs/guides/serialization/data-types) diff --git a/doc/docs/guides/game-objects/lifecycle.md b/doc/docs/guides/game-objects/lifecycle.md index 7906e7cc3bf..b4fb11b21eb 100644 --- a/doc/docs/guides/game-objects/lifecycle.md +++ b/doc/docs/guides/game-objects/lifecycle.md @@ -53,20 +53,7 @@ To start a server object, [spawn it](/docs/guides/game-objects/spawn-object). If For example: -```cs -public class MyComponent : MonoBehaviour -{ - public void Awake() - { - GetComponent.OnStartServer.AddListener(OnStartServer); - } - - public void OnStartServer() - { - Debug.Log("The object started on the server") - } -} -``` +{{{ Path:'Snippets/GameObjects/LifecycleComponent.cs' Name:'lifecycle-start-server' }}} You can also simply drag your `OnStartServer` method in the [NetworkIdentity.OnStartServer](/docs/reference/Mirage/NetworkIdentity#onstartserver) event in the inspector. @@ -79,49 +66,7 @@ The NetworkWorld class is what holds the list of all spawned Identities. This cl NetworkWorld has event that are called when Network objects are spawned or unspawn, they can be used when you need to do this on all network objects, but dont want to add listeners to each one individually. -```cs -public class MyComponent : MonoBehaviour -{ - public NetworkServer Server; - public NetworkClient Client; - - public void Awake() - { - // Client/Server.World is only set after server is started, - // so wait for start, then add event listener to OnSpawn - Server.Started.AddListener(ServerStarted); - Client.Started.AddListener(ClientStarted); - } - - private void ServerStarted() - { - Server.World.onSpawn += OnServerSpawn; - Server.World.onUnspawn += OnServerUnspawn; - } - private void OnServerSpawn(NetworkIdentity identity) - { - Debug.Log($"The object {identity} was spawned on the server"); - } - private void OnServerUnspawn(uint netId, NetworkIdentity identity) - { - Debug.Log($"The object {identity} (netId={netId}) was unspawned on the server"); - } - - private void ClientStarted() - { - Client.World.onSpawn += OnClientSpawn; - Client.World.onUnspawn += OnClientUnspawn; - } - private void OnClientSpawn(NetworkIdentity identity) - { - Debug.Log($"The object {identity} was spawned on the client"); - } - private void OnClientUnspawn(uint netId, NetworkIdentity identity) - { - Debug.Log($"The object {identity} (netId={netId}) was unspawned on the client"); - } -} -``` +{{{ Path:'Snippets/GameObjects/NetworkWorldEvents.cs' Name:'network-world-events' }}} ## Client Instantiate diff --git a/doc/docs/guides/game-objects/pickup-drop-child.md b/doc/docs/guides/game-objects/pickup-drop-child.md index 82453bd9984..7f83ba83059 100644 --- a/doc/docs/guides/game-objects/pickup-drop-child.md +++ b/doc/docs/guides/game-objects/pickup-drop-child.md @@ -23,82 +23,7 @@ Below is the Player Equip script to handle the changing of the equipped item, an - While we could just have all the art items attached at design time and just enable/disable them based on the enum, this doesn't scale well to a lot of items and if they have scripts on them for how they behave in the game, such as animations, special effects, etc. it could get ugly pretty fast, so this example locally instantiates and destroys instead as a design choice. - The example makes no effort to deal with position offset between the item and the attach point, e.g. having the grip or handle of an item aligns with the hand. This is best dealt with in a MonoBehaviour script on the item that has public fields for the local position and rotation that can be set in the designer and a bit of code in Start to apply those values in local coordinates relative to the parent attach point. -``` cs -using UnityEngine; -using System.Collections; -using Mirage; - -public enum EquippedItem : byte -{ - nothing, - ball, - box, - cylinder -} - -public class PlayerEquip : NetworkBehaviour -{ - public GameObject sceneObjectPrefab; - - public GameObject rightHand; - - public GameObject ballPrefab; - public GameObject boxPrefab; - public GameObject cylinderPrefab; - - [SyncVar(hook = nameof(OnChangeEquipment))] - public EquippedItem equippedItem { get; set; } - - void OnChangeEquipment(EquippedItem oldEquippedItem, EquippedItem newEquippedItem) - { - StartCoroutine(ChangeEquipment(newEquippedItem)); - } - - // Since Destroy is delayed to the end of the current frame, we use a coroutine - // to clear out any child objects before instantiating the new one - IEnumerator ChangeEquipment(EquippedItem newEquippedItem) - { - while (rightHand.transform.childCount > 0) - { - Destroy(rightHand.transform.GetChild(0).gameObject); - yield return null; - } - - switch (newEquippedItem) - { - case EquippedItem.ball: - Instantiate(ballPrefab, rightHand.transform); - break; - case EquippedItem.box: - Instantiate(boxPrefab, rightHand.transform); - break; - case EquippedItem.cylinder: - Instantiate(cylinderPrefab, rightHand.transform); - break; - } - } - - void Update() - { - if (!IsLocalPlayer) return; - - if (Input.GetKeyDown(KeyCode.Alpha0) && equippedItem != EquippedItem.nothing) - CmdChangeEquippedItem(EquippedItem.nothing); - if (Input.GetKeyDown(KeyCode.Alpha1) && equippedItem != EquippedItem.ball) - CmdChangeEquippedItem(EquippedItem.ball); - if (Input.GetKeyDown(KeyCode.Alpha2) && equippedItem != EquippedItem.box) - CmdChangeEquippedItem(EquippedItem.box); - if (Input.GetKeyDown(KeyCode.Alpha3) && equippedItem != EquippedItem.cylinder) - CmdChangeEquippedItem(EquippedItem.cylinder); - } - - [ServerRpc] - void CmdChangeEquippedItem(EquippedItem selectedItem) - { - equippedItem = selectedItem; - } -} -``` +{{{ Path:'Snippets/GameObjects/PlayerEquipInitial.cs' Name:'player-equip-initial' }}} ## Dropping Items @@ -106,107 +31,13 @@ Now that we can equip the items, we need a way to drop the current item into the First, let's add one more Input to the Update method above and a `CmdDropItem` method: -``` cs -void Update() -{ - if (!IsLocalPlayer) return; - - if (Input.GetKeyDown(KeyCode.Alpha0) && equippedItem != EquippedItem.nothing) - CmdChangeEquippedItem(EquippedItem.nothing); - if (Input.GetKeyDown(KeyCode.Alpha1) && equippedItem != EquippedItem.ball) - CmdChangeEquippedItem(EquippedItem.ball); - if (Input.GetKeyDown(KeyCode.Alpha2) && equippedItem != EquippedItem.box) - CmdChangeEquippedItem(EquippedItem.box); - if (Input.GetKeyDown(KeyCode.Alpha3) && equippedItem != EquippedItem.cylinder) - CmdChangeEquippedItem(EquippedItem.cylinder); - - if (Input.GetKeyDown(KeyCode.X) && equippedItem != EquippedItem.nothing) - CmdDropItem(); -} -``` - -``` cs -[ServerRpc] -void CmdDropItem() -{ - // Instantiate the scene object on the server - Vector3 pos = rightHand.transform.position; - Quaternion rot = rightHand.transform.rotation; - GameObject newSceneObject = Instantiate(sceneObjectPrefab, pos, rot); - - // set the RigidBody as non-kinematic on the server only (isKinematic = true in prefab) - newSceneObject.GetComponent().isKinematic = false; - - SceneObject sceneObject = newSceneObject.GetComponent(); - - // set the child object on the server - sceneObject.SetEquippedItem(equippedItem); - - // set the SyncVar on the scene object for clients - sceneObject.equippedItem = equippedItem; - - // set the player's SyncVar to nothing so clients will destroy the equipped child item - equippedItem = EquippedItem.nothing; - - // Spawn the scene object on the network for all to see - ServerObjectManager.Spawn(newSceneObject); -} -``` +{{{ Path:'Snippets/GameObjects/PlayerEquip.cs' Name:'player-equip-update' }}} + +{{{ Path:'Snippets/GameObjects/PlayerEquip.cs' Name:'player-equip-drop' }}} In the image above, there's a `sceneObjectPrefab` field that is assigned to a prefab that will act as a container for our item prefabs. The SceneObject prefab has a SceneObject script with a SyncVar like the Player Equip script, and a SetEquippedItem method that takes the shared enum value as a parameter. -``` cs -using UnityEngine; -using System.Collections; -using Mirage; - -public class SceneObject : NetworkBehaviour -{ - [SyncVar(hook = nameof(OnChangeEquipment))] - public EquippedItem equippedItem { get; set; } - - public GameObject ballPrefab; - public GameObject boxPrefab; - public GameObject cylinderPrefab; - - void OnChangeEquipment(EquippedItem oldEquippedItem, EquippedItem newEquippedItem) - { - StartCoroutine(ChangeEquipment(newEquippedItem)); - } - - // Since Destroy is delayed to the end of the current frame, we use a coroutine - // to clear out any child objects before instantiating the new one - IEnumerator ChangeEquipment(EquippedItem newEquippedItem) - { - while (transform.childCount > 0) - { - Destroy(transform.GetChild(0).gameObject); - yield return null; - } - - // Use the new value, not the SyncVar property value - SetEquippedItem(newEquippedItem); - } - - // SetEquippedItem is called on the client from OnChangeEquipment (above), - // and on the server from CmdDropItem in the PlayerEquip script. - public void SetEquippedItem(EquippedItem newEquippedItem) - { - switch (newEquippedItem) - { - case EquippedItem.ball: - Instantiate(ballPrefab, transform); - break; - case EquippedItem.box: - Instantiate(boxPrefab, transform); - break; - case EquippedItem.cylinder: - Instantiate(cylinderPrefab, transform); - break; - } - } -} -``` +{{{ Path:'Snippets/GameObjects/SceneObject.cs' Name:'scene-object-class' }}} In the run-time image below, the Ball(Clone) is attached to the `RightHand` object, and the Box(Clone) is attached to the SceneObject(Clone), which is shown in the inspector. @@ -218,27 +49,11 @@ The art prefabs have simple colliders on them (sphere, box, capsule). If your a Now that we have a box dropped in the scene, we need to pick it up again. To do that, a `CmdPickupItem` method is added to the Player Equip script: -``` cs -// CmdPickupItem is public because it's called from a script on the SceneObject -[ServerRpc] -public void CmdPickupItem(GameObject sceneObject) -{ - // set the player's SyncVar so clients can show the equipped item - equippedItem = sceneObject.GetComponent().equippedItem; - - // Destroy the scene object - ServerObjectManager.Destroy(sceneObject); -} -``` +{{{ Path:'Snippets/GameObjects/PlayerEquip.cs' Name:'player-equip-pickup' }}} This method is simply called from `OnMouseDown` in the Scene Object script: -```cs -private void OnMouseDown() -{ - Client.Player.Identity.GetComponent().CmdPickupItem(gameObject); -} -``` +{{{ Path:'Snippets/GameObjects/SceneObject.cs' Name:'scene-object-mousedown' }}} Since the SceneObject(Clone) is networked, we can pass it directly through to `CmdPickupItem` on the character object to set the equipped item SyncVar and destroy the scene object. diff --git a/doc/docs/guides/game-objects/player-proxy-pattern.md b/doc/docs/guides/game-objects/player-proxy-pattern.md index b1608266354..15026edfe3c 100644 --- a/doc/docs/guides/game-objects/player-proxy-pattern.md +++ b/doc/docs/guides/game-objects/player-proxy-pattern.md @@ -22,62 +22,25 @@ With a proxy object, the player's persistent state lives on a simple `NetworkBeh Create a prefab with just a `NetworkIdentity` and your `PlayerContext` script. This prefab does not need any visual representation. -```cs -public class PlayerContext : NetworkBehaviour -{ - [SyncVar] public string PlayerName { get; set; } - [SyncVar] public string Team { get; set; } - - // easy access to the gameplay character via NetworkPlayer.Identity - [SyncVar] public PlayerCharacter ActiveCharacter { get; set; } -} -``` +{{{ Path:'Snippets/GameObjects/PlayerContext.cs' Name:'player-proxy-context' }}} ### 2. Create the Gameplay Character Prefab This is your normal player character with movement, visuals, etc. It does not use `IsLocalPlayer` — instead it uses `HasAuthority` to check for local control. -```cs -public class PlayerCharacter : NetworkBehaviour -{ - void Update() - { - // use HasAuthority, not IsLocalPlayer - if (!HasAuthority) - return; - - // handle input and movement - } -} -``` +{{{ Path:'Snippets/GameObjects/PlayerCharacter.cs' Name:'player-proxy-character' }}} ### 3. Spawn the Proxy on Connect When a player connects, spawn the proxy as their character using `AddCharacter`. This proxy persists for the entire session. -```cs -private void OnServerAuthenticated(INetworkPlayer player) -{ - var proxy = Instantiate(PlayerProxyPrefab); - ServerObjectManager.AddCharacter(player, proxy.gameObject); -} -``` +{{{ Path:'Snippets/GameObjects/PlayerProxyManager.cs' Name:'player-proxy-spawn-proxy' }}} ### 4. Spawn the Gameplay Character Separately When entering gameplay (e.g. after scene load, match start), spawn the gameplay character and grant authority to the player. -```cs -private void SpawnGameplayCharacter(INetworkPlayer player) -{ - var proxy = player.Identity.GetComponent(); - - var character = Instantiate(CharacterPrefab, spawnPoint.position, spawnPoint.rotation); - ServerObjectManager.Spawn(character.Identity, player); - - proxy.ActiveCharacter = character; -} -``` +{{{ Path:'Snippets/GameObjects/PlayerProxyManager.cs' Name:'player-proxy-spawn-character' }}} :::note The gameplay character is spawned with `Spawn(identity, owner)` which grants authority to the player, rather than `AddCharacter` which would replace the proxy. @@ -87,17 +50,4 @@ The gameplay character is spawned with `Spawn(identity, owner)` which grants aut Because the proxy is the player's `Identity`, you can destroy and re-create gameplay characters without affecting the player's connection or persistent state. -```cs -private void RespawnCharacter(INetworkPlayer player) -{ - var proxy = player.Identity.GetComponent(); - - if (proxy.ActiveCharacter != null) - { - Server.Destroy(proxy.ActiveCharacter.gameObject); - proxy.ActiveCharacter = null; - } - - SpawnGameplayCharacter(player); -} -``` +{{{ Path:'Snippets/GameObjects/PlayerProxyManager.cs' Name:'player-proxy-respawn' }}} diff --git a/doc/docs/guides/game-objects/scene-objects.md b/doc/docs/guides/game-objects/scene-objects.md index a272bf1080e..c9a77af959b 100644 --- a/doc/docs/guides/game-objects/scene-objects.md +++ b/doc/docs/guides/game-objects/scene-objects.md @@ -33,37 +33,4 @@ In some cases, like when running multiple server or client instances in the same To solve this, you can provide a custom filter by setting the `SceneObjectFilter` property on the `ServerObjectManager` and `ClientObjectManager`. This allows you to control exactly which `NetworkIdentity` components are included. If the filter is left `null`, the default behavior is used. **Example: Only include objects from a specific scene** -```csharp -using Mirage; -using UnityEngine; -using UnityEngine.SceneManagement; - -public class MySceneManager : MonoBehaviour -{ - public ServerObjectManager serverObjectManager; - public ClientObjectManager clientObjectManager; - public Scene myScene; - - // Set the scene to use for filtering - public void SetScene(Scene scene) - { - myScene = scene; - } - - void Awake() - { - // Set the filter before spawning scene objects - var filter = (NetworkIdentity identity) => - { - return identity.gameObject.scene == myScene; - }; - - serverObjectManager.SceneObjectFilter = filter; - clientObjectManager.SceneObjectFilter = filter; - - // Now when SpawnSceneObjects is called, it will only - // consider objects from `myScene`. - serverObjectManager.SpawnSceneObjects(); - } -} -``` +{{{ Path:'Snippets/GameObjects/SceneObjectFilterExample.cs' Name:'scene-object-filter-example' }}} diff --git a/doc/docs/guides/game-objects/spawn-object-custom.md b/doc/docs/guides/game-objects/spawn-object-custom.md index 9a4f9f4a6fa..c334a68185b 100644 --- a/doc/docs/guides/game-objects/spawn-object-custom.md +++ b/doc/docs/guides/game-objects/spawn-object-custom.md @@ -11,64 +11,29 @@ Use `ClientObjectManager.RegisterSpawnHandler` or `ClientObjectManager.RegisterP The spawn/unspawn delegates will look something like this: **Spawn Handler** -``` cs -NetworkIdentity SpawnDelegate(SpawnMessage msg) -{ - // do stuff here -} -``` +{{{ Path:'Snippets/GameObjects/CustomSpawnExample.cs' Name:'spawn-handler-delegate' }}} **UnSpawn Handler** -```cs -void UnSpawnDelegate(NetworkIdentity spawned) -{ - // do stuff here -} -``` +{{{ Path:'Snippets/GameObjects/CustomSpawnExample.cs' Name:'unspawn-handler-delegate' }}} When a prefab is saved its `PrefabHash` field will be automatically set. If you want to create prefabs at runtime you will have to generate a new Hash instead. **Generate prefab at runtime** -``` cs -// Create a hash that can be generated on both server and client -// using a string and GetStableHashCode is a good way to do this -int coinHash = "MyCoin".GetStableHashCode(); - -// register handlers using hash -ClientObjectManager.RegisterSpawnHandler(coinHash, SpawnCoin, UnSpawnCoin); -``` +{{{ Path:'Snippets/GameObjects/CustomSpawnExample.cs' Name:'generate-prefab-runtime' }}} :::note The unspawn function may be left as `null`, Mirage will then call `GameObject.Destroy` when the destroy message is received. ::: **Use existing prefab** -```cs -// register handlers using prefab -ClientObjectManager.RegisterPrefab(coin, SpawnCoin, UnSpawnCoin); -``` +{{{ Path:'Snippets/GameObjects/CustomSpawnExample.cs' Name:'use-existing-prefab' }}} **Spawn on Server** -```cs -int coinHash = "MyCoin".GetStableHashCode(); - -// spawn a coin - SpawnCoin is called on client -// pass in coinHash so that it is set on the Identity before it is sent to client -NetworkServer.Spawn(gameObject, coinHash); -``` +{{{ Path:'Snippets/GameObjects/CustomSpawnExample.cs' Name:'spawn-on-server' }}} The spawn functions themselves are implemented with the delegate signature. Here is the coin spawner. The `SpawnCoin` would look the same, but have different spawn logic: -``` cs -public NetworkIdentity SpawnCoin(SpawnMessage msg) -{ - return Instantiate(m_CoinPrefab, msg.position, msg.rotation); -} -public void UnSpawnCoin(NetworkIdentity spawned) -{ - Destroy(spawned); -} -``` +{{{ Path:'Snippets/GameObjects/CustomSpawnExample.cs' Name:'spawn-coin-methods' }}} When using custom spawn functions, it is sometimes useful to be able to unspawn game objects without destroying them. This can be done by calling `NetworkServer.Destroy(identity, destroyServerObject: false)`, making sure that the 2nd argument is false. This causes the object to be `Reset` on the server and sends a `ObjectDestroyMessage` to clients. The `ObjectDestroyMessage` will cause the custom unspawn function to be called on the clients. If there is no unspawn function the object will instead be `Destroy` @@ -80,24 +45,7 @@ you can use custom spawn handlers in order set up object pooling so you dont nee A full guide on pooling can be found here: [Spawn Object Pooling](./spawn-object-pooling) -```cs -void ClientConnected() -{ - clientObjectManager.RegisterPrefab(prefab, PoolSpawnHandler, PoolUnspawnHandler); -} - -// used by clientObjectManager.RegisterPrefab -NetworkIdentity PoolSpawnHandler(SpawnMessage msg) -{ - return GetFromPool(msg.position, msg.rotation); -} - -// used by clientObjectManager.RegisterPrefab -void PoolUnspawnHandler(NetworkIdentity spawned) -{ - PutBackInPool(spawned); -} -``` +{{{ Path:'Snippets/GameObjects/CustomSpawnExample.cs' Name:'pool-spawn-handlers' }}} ## Dynamic spawning @@ -107,4 +55,4 @@ Below is an example where client pre-spawns objects while loading, and then netw Dynamic Handler avoid the need to add 1 spawn handler for each prefab hash. Instead you can just add a single dynamic handler that can then be used to find and return objects. -{{{ Path:'Snippets/DynamicSpawning.cs' Name:'dynamic-spawning' }}} \ No newline at end of file +{{{ Path:'Snippets/Spawning/DynamicSpawning.cs' Name:'dynamic-spawning' }}} \ No newline at end of file diff --git a/doc/docs/guides/game-objects/spawn-object.md b/doc/docs/guides/game-objects/spawn-object.md index c64ded2904b..1e270b35a91 100644 --- a/doc/docs/guides/game-objects/spawn-object.md +++ b/doc/docs/guides/game-objects/spawn-object.md @@ -7,10 +7,7 @@ title: Spawn Object In Unity, you usually “spawn” (that is, create) new game objects with `Instantiate`. However, in Mirage, the word “spawn” means something more specific. In the server-authoritative model of the Mirage, to “spawn” a game object on the server means that the game object is created on clients connected to the server, and is managed by the spawning system. To spawn an object on the server you need to `Instantiate` the prefab and then call `Spawn` on the new object. This will assign a `NetId` to the object and send a `SpawnMessage` to clients. -```cs -var boxGo = Instantiate(boxPrefab); -ServerObjectManager.Spawn(boxGo); -``` +{{{ Path:'Snippets/GameObjects/SpawningExample.cs' Name:'spawn-box-example' }}} Once the game object is spawned using this system, state updates are sent to clients whenever the game object changes on the server. When Mirage destroys the game object on the server, it also destroys it on the clients. The server manages spawned game objects alongside all other networked game objects so that if another client joins the game later, the server can spawn the game objects on that client. These spawned game objects have a unique network instance ID called `NetId` that is the same on the server and clients for each game object. The unique network instance ID is used to route messages sent across the network to game objects and to identify game objects. @@ -33,40 +30,7 @@ For more advanced users, you may find that you want to register Prefabs and spaw To spawn game objects without using the Network Manager, you can handle the Prefab registration yourself via script. Use the `ClientScene.RegisterPrefab` method to register Prefabs to the Network Manager. -``` cs -using UnityEngine; -using Mirage; - -public class MyNetworkManager : MonoBehaviour -{ - public GameObject treePrefab; - public ClientObjectManager; - public NetworkClient; - public NetworkServer; - public ServerObjectManager; - - void Start() - { - ClientObjectManager = FindObjectOfType(); - NetworkClient = FindObjectOfType(); - NetworkServer = FindObjectOfType(); - ServerObjectManager = FindObjectOfType(); - } - - // Register prefab and connect to the server - public void ClientConnect() - { - ClientObjectManager.spawnPrefabs.Add(treePrefab); - NetworkClient.Connect("localhost"); - NetworkClient.MessageHandler.RegisterHandler(OnClientConnect); - } - - void OnClientConnect(NetworkConnection conn, ConnectMessage msg) - { - Debug.Log("Connected to server: " + conn); - } -} -``` +{{{ Path:'Snippets/GameObjects/MyNetworkManager.cs' Name:'spawning-without-network-manager-1' }}} In this example, you create an empty game object to act as the Network Manager, then create and attach the `MyNetworkManager` script (above) to that game object. Create a prefab that has a Network Identity component attached to it, and drag that onto the `treePrefab` slot on the `MyNetworkManager` component in the Inspector. This ensures that when the server spawns the tree game object, it also creates the same kind of game object on the clients. @@ -74,38 +38,7 @@ Registering prefabs ensures that there is no stalling or loading time for creati For the script to work, you also need to add code for the server. Add this to the `MyNetworkManager` script: -``` cs -public void ServerListen() -{ - // start listening, and allow up to 4 connections - NetworkServer.StartServer(); - - NetworkServer.MessageHandler.RegisterHandler(OnServerConnect); - NetworkServer.MessageHandler.RegisterHandler(OnClientReady); -} - -// When client is ready spawn a few trees -void OnClientReady(NetworkConnection conn, ReadyMessage msg) -{ - Debug.Log("Client is ready to start: " + conn); - SpawnTrees(); -} - -void SpawnTrees() -{ - int x = 0; - for (int i = 0; i < 5; ++i) - { - GameObject treeGo = Instantiate(treePrefab, new Vector3(x++, 0, 0), Quaternion.identity); - ServerObjectManager.Spawn(treeGo); - } -} - -void OnServerConnect(NetworkConnection conn, ConnectMessage msg) -{ - Debug.Log("New client connected: " + conn); -} -``` +{{{ Path:'Snippets/GameObjects/MyNetworkManager.cs' Name:'spawning-without-network-manager-2' }}} The server does not need to register anything, as it knows what game object is being spawned (and the asset ID is sent in the spawn message). The client needs to be able to look up the game object, so it must be registered on the client. @@ -115,43 +48,11 @@ For more advanced uses, such as object pools or dynamically created Assets, you If the game object has a network state like synchronized variables, then that state is synchronized with the spawn message. In the following example, this script is attached to the tree Prefab: -``` cs -using UnityEngine; -using Mirage; - -public class Tree : NetworkBehaviour -{ - [SyncVar] - public int numLeaves { get; set; } - - void Start() - { - Identity.OnStartClient.AddLisenter(OnStartClient); - } - - public override void OnStartClient() - { - Debug.Log("Tree spawned with leaf count " + numLeaves); - } -} -``` +{{{ Path:'Snippets/GameObjects/Tree.cs' Name:'tree-syncvar-example' }}} With this script attached, you can change the `numLeaves` variable and modify the `SpawnTrees` function to see it accurately reflected on the client: -``` cs -void SpawnTrees() -{ - int x = 0; - for (int i = 0; i < 5; ++i) - { - GameObject treeGo = Instantiate(treePrefab, new Vector3(x++, 0, 0), Quaternion.identity); - Tree tree = treeGo.GetComponent(); - tree.numLeaves = Random.Range(10,200); - Debug.Log("Spawning leaf with leaf count " + tree.numLeaves); - ServerObjectManager.Spawn(treeGo); - } -} -``` +{{{ Path:'Snippets/GameObjects/SpawningExample.cs' Name:'spawn-trees-example' }}} Attach the `Tree` script to the `treePrefab` script created earlier to see this in action. @@ -203,43 +104,10 @@ For these game objects, the property `HasAuthority` is true on the client with a For example, the tree spawn example above can be modified to allow the tree to have client authority like this (note that we now need to pass in a Network Player game object for the owning client’s connection): -``` cs -void SpawnTrees(INetworkPlayer player) -{ - int x = 0; - for (int i = 0; i < 5; ++i) - { - GameObject treeGo = Instantiate(treePrefab, new Vector3(x++, 0, 0), Quaternion.identity); - Tree tree = treeGo.GetComponent(); - tree.numLeaves = Random.Range(10,200); - Debug.Log("Spawning leaf with leaf count " + tree.numLeaves); - ServerObjectManager.Spawn(treeGo, player); - } -} -``` +{{{ Path:'Snippets/GameObjects/SpawningExample.cs' Name:'spawn-trees-authority-example' }}} The Tree script can now be modified to send a Server RPC Call to the server: -``` cs - public void ClientConnect() - { - ClientObjectManager.spawnPrefabs.Add(treePrefab); - NetworkClient.Connect("localhost"); - NetworkClient.MessageHandler.RegisterHandler(OnClientConnect); - - NetworkClient.Player.Identity.OnAuthorityChanged.AddListener(OnStartAuthority); - } - -public override void OnStartAuthority(bool changed) -{ - CmdMessageFromTree("Tree with " + numLeaves + " reporting in"); -} - -[ServerRpc] -void CmdMessageFromTree(string msg) -{ - Debug.Log("Client sent a tree message: " + msg); -} -``` +{{{ Path:'Snippets/GameObjects/Tree.cs' Name:'tree-client-authority' }}} Note that you can’t just add the `CmdMessageFromTree` call into `OnStartClient`, because at that point the authority has not been set yet, so the call would fail. diff --git a/doc/docs/guides/game-objects/spawn-player-custom.md b/doc/docs/guides/game-objects/spawn-player-custom.md index 45f4a67e807..0aa56a77245 100644 --- a/doc/docs/guides/game-objects/spawn-player-custom.md +++ b/doc/docs/guides/game-objects/spawn-player-custom.md @@ -16,128 +16,25 @@ In this case, you will need to create your own CharacterSpawner. Follow these s 1) Create your player prefabs (as many as you need) and add them to the Spawnable Prefabs in your ClientObjectManager. 2) Create a message that describes your player. For example: -``` cs -public struct CreateMMOCharacterMessage -{ - public Race race; - public string name; - public Color hairColor; - public Color eyeColor; -} - -public enum Race -{ - Human, - Elvish, - Dwarvish, -} -``` +{{{ Path:'Snippets/GameObjects/CustomPlayerSpawning.cs' Name:'create-mmo-character-message' }}} 3) Create Player Spawner class and add it to some GameObject in your scene -``` cs -public class CustomCharacterSpawner : MonoBehaviour -{ - [Header("References")] - public NetworkClient Client; - public NetworkServer Server; - public ClientObjectManager ClientObjectManager; - public ServerObjectManager ServerObjectManager; - - [Header("Prefabs")] - // Different prefabs based on the Race the player picks - public CustomCharacter HumanPrefab; - public CustomCharacter ElvishPrefab; - public CustomCharacter DwarvishPrefab; -} -``` +{{{ Path:'Snippets/GameObjects/CustomPlayerSpawning.cs' Name:'custom-character-spawner-class' }}} 4) Drag the NetworkClient and NetworkServer and Scene manager to the fields 5) Hook into events: -```cs -public void Start() -{ - Client.Started.AddListener(OnClientStarted); - Client.Authenticated.AddListener(OnClientAuthenticated); - Server.Started.AddListener(OnServerStarted); -} -``` +{{{ Path:'Snippets/GameObjects/CustomPlayerSpawning.cs' Name:'custom-character-spawner-start' }}} 6) register the prefabs when the client starts -```cs -private void OnClientStarted() -{ - // Make sure all prefabs are Register so mirage can spawn the character for this client and for other players - ClientObjectManager.RegisterPrefab(HumanPrefab.Identity); - ClientObjectManager.RegisterPrefab(ElvishPrefab.Identity); - ClientObjectManager.RegisterPrefab(DwarvishPrefab.Identity); -} -``` +{{{ Path:'Snippets/GameObjects/CustomPlayerSpawning.cs' Name:'custom-character-spawner-client-started' }}} 7) Send your message with your character data when your client connects, or after the user submits his preferences. -``` cs -// You can send the message here if you already know -// everything about the character at the time of player -// or at a later time when the user submits his preferences -private void OnClientAuthenticated(INetworkPlayer player) -{ - var mmoCharacter = new CreateMMOCharacterMessage - { - // populate the message with your data - name = "player user name", - race = Race.Human, - eyeColor = Color.red, - hairColor = Color.black, - }; - player.Send(mmoCharacter); -} -``` +{{{ Path:'Snippets/GameObjects/CustomPlayerSpawning.cs' Name:'custom-character-spawner-client-authenticated' }}} 8) Receive your message in the server and spawn the player -```cs -private void OnServerStarted() -{ - // Wait for client to send us an AddPlayerMessage - Server.MessageHandler.RegisterHandler(OnCreateCharacter); -} - -private void OnCreateCharacter(INetworkPlayer player, CreateMMOCharacterMessage msg) -{ - CustomCharacter prefab = GetPrefab(msg); - - // Create your character object - // Use the data in msg to configure it - CustomCharacter character = Instantiate(prefab); - - // Set syncVars before telling Mirage to spawn character - // This will cause them to be sent to client in the spawn message - character.PlayerName = msg.name; - character.hairColor = msg.hairColor; - character.eyeColor = msg.eyeColor; - - // Spawn it as the character object - ServerObjectManager.AddCharacter(player, character.Identity); -} - -private CustomCharacter GetPrefab(CreateMMOCharacterMessage msg) -{ - // Get prefab based on race - CustomCharacter prefab; - switch (msg.race) - { - case Race.Human: prefab = HumanPrefab; break; - case Race.Elvish: prefab = ElvishPrefab; break; - case Race.Dwarvish: prefab = DwarvishPrefab; break; - // Default case to check that client sent valid race. - // The only reason it should be invalid is if the client's code was modified by an attacker - // Throw will cause the client to be kicked - default: throw new InvalidEnumArgumentException("Invalid race given"); - } - - return prefab; -} -``` +{{{ Path:'Snippets/GameObjects/CustomPlayerSpawning.cs' Name:'custom-character-spawner-server-started' }}} ## Ready State @@ -163,39 +60,13 @@ To replace the character game object for a player, use `ServerObjectManager.Repl You can also use `ReplaceCharacter` to respawn a player or change the object that represents the player. In some cases, it is better to just disable a game object and reset its game attributes on respawn. The following code sample demonstrates how to replace the player game object with a new game object: -``` cs -public class CustomCharacterSpawner : MonoBehaviour -{ - public NetworkServer Server; - public ServerObjectManager ServerObjectManager; - - public void Respawn(NetworkPlayer player, GameObject newPrefab) - { - // Cache a reference to the current character object - GameObject oldPlayer = player.Identity.gameObject; - - var newCharacter = Instantiate(newPrefab); - - // Instantiate the new character object and broadcast to clients - // NOTE: here we can use `keepAuthority: true` because we are calling Destroy on the old prefab immediately after. - ServerObjectManager.ReplaceCharacter(player, newCharacter, keepAuthority: true); - - // Remove the previous character object that's now been replaced - Server.Destroy(oldPlayer); - } -} -``` +{{{ Path:'Snippets/GameObjects/CustomPlayerSpawning.cs' Name:'custom-character-spawner-respawn' }}} ## Destroying Characters Once the character is finished (eg game over, or player died) you can remove the character using `ServerObjectManager.DestroyCharacter`. -```cs -public void OnPlayerDeath(INetworkPlayer player) -{ - ServerObjectManager.DestroyCharacter(player); -} -``` +{{{ Path:'Snippets/GameObjects/CustomPlayerSpawning.cs' Name:'custom-character-spawner-death' }}} Alternatively, you can use `ServerObjectManager.RemoveCharacter` to remove it as the player's character without destroying it. diff --git a/doc/docs/guides/remote-actions/client-rpc.md b/doc/docs/guides/remote-actions/client-rpc.md index ee4d9903f8b..5da43605519 100644 --- a/doc/docs/guides/remote-actions/client-rpc.md +++ b/doc/docs/guides/remote-actions/client-rpc.md @@ -7,13 +7,7 @@ ClientRpcs are sent from [NetworkBehaviours](/docs/reference/Mirage/NetworkBehav To make a function into a ClientRpc add [`[ClientRpc]`](/docs/reference/Mirage/ClientRpcAttribute) directly above the function. -```cs -[ClientRpc] -public void MyRpcFunction() -{ - // Code to invoke on client -} -``` +{{{ Path:'Snippets/RemoteActions/ClientRpcExamples.cs' Name:'client-rpc-attribute' }}} ClientRpc functions can't be static and must return `void`. @@ -38,13 +32,7 @@ This will send the RPC message to only the owner of the object. This will send the RPC message to the [`NetworkPlayer`](/docs/reference/Mirage/NetworkPlayer) that is passed into the call. -```cs -[ClientRpc(target = RpcTarget.Player)] -public void MyRpcFunction(NetworkPlayer target) -{ - // Code to invoke on client -} -``` +{{{ Path:'Snippets/RemoteActions/ClientRpcExamples.cs' Name:'client-rpc-player' }}} Mirage will use the `NetworkPlayer target` to know where to send it, but it will not send the `target` value. Because of this, its value will always be null for the client. @@ -63,33 +51,11 @@ ClientRpcs can return values only if RpcTarget is `Player` or `Owner`. It can ta To return a value, add a return value using `UniTask` where `MyReturnType` is any [supported Mirage type](/docs/guides/serialization/data-types). In the client, you can make your method async, or you can use `UniTask.FromResult(myResult);`. For example: -{{{ Path:'Snippets/RpcReply.cs' Name:'client-rpc-reply' }}} +{{{ Path:'Snippets/Rpc/RpcReply.cs' Name:'client-rpc-reply' }}} # Examples -``` cs -public class Player : NetworkBehaviour -{ - private int health; - - public void TakeDamage(int amount) - { - if (!IsServer) - { - return; - } - - health -= amount; - Damage(amount); - } - - [ClientRpc] - private void Damage(int amount) - { - Debug.Log("Took damage:" + amount); - } -} -``` +{{{ Path:'Snippets/RemoteActions/ClientRpcExamples.cs' Name:'client-rpc-example-health' }}} When running a game as a host with a local client, ClientRpc calls will be invoked on the local client even though it is in the same process as the server. So the behaviors of local and remote clients are the same for ClientRpc calls. @@ -97,44 +63,8 @@ You can also specify which client gets the call with the `target` parameter. If you only want the client that owns the object to be called, use `[ClientRpc(target = RpcTarget.Owner)]` or you can specify which client gets the message by using `[ClientRpc(target = RpcTarget.Player)]` and passing the player as a parameter. For example: -``` cs -public class Player : NetworkBehaviour -{ - private int health; - - [Server] - private void Magic(GameObject target, int damage) - { - target.GetComponent().health -= damage; - - NetworkIdentity opponentIdentity = target.GetComponent(); - DoMagic(opponentIdentity.Owner, damage); - } - - [ClientRpc(target = RpcTarget.Player)] - public void DoMagic(INetworkPlayer target, int damage) - { - // This will appear on the opponent's client, not the attacking player's - Debug.Log($"Magic Damage = {damage}"); - } - - [Server] - private void HealMe() - { - health += 10; - Healed(10); - } - - [ClientRpc(target = RpcTarget.Owner)] - public void Healed(int amount) - { - // No NetworkPlayer parameter, so it goes to owner - Debug.Log($"Health increased by {amount}"); - } -} -``` +{{{ Path:'Snippets/RemoteActions/ClientRpcExamples.cs' Name:'client-rpc-example-magic' }}} ## Parameter Size Limits (MaxLength Attribute) Just like ServerRpcs, you can use the `[MaxLength(int)]` attribute on `ClientRpc` string and collection parameters to restrict the maximum allowed size of incoming payloads during deserialization. See the [Server Rpc MaxLength documentation](/docs/guides/remote-actions/server-rpc#protecting-against-memory-allocation-attacks-maxlength-attribute) for details. - diff --git a/doc/docs/guides/remote-actions/network-messages.md b/doc/docs/guides/remote-actions/network-messages.md index ecc8644e8fd..2a6da8e7d8a 100644 --- a/doc/docs/guides/remote-actions/network-messages.md +++ b/doc/docs/guides/remote-actions/network-messages.md @@ -11,7 +11,7 @@ For the most part, we recommend the high-level [ServerRpc](/docs/guides/remote-a 4. Use the `Send()` method on the [NetworkClient](/docs/reference/Mirage/NetworkClient), [NetworkServer](/docs/reference/Mirage/NetworkServer), or [NetworkPlayer](/docs/reference/Mirage/NetworkPlayer) classes depending on which way you want to send the message. ## Example -{{{ Path:'Snippets/SendNetworkMessage.cs' Name:'send-score' }}} +{{{ Path:'Snippets/Messaging/SendNetworkMessage.cs' Name:'send-score' }}} Note that there is no serialization code for the `ScoreMessage` struct in this source code example. Mirage will generate a reader and writer for ScoreMessage when it sees that it is being sent. diff --git a/doc/docs/guides/remote-actions/rate-limiting.md b/doc/docs/guides/remote-actions/rate-limiting.md index d76fc2d5a4e..ba344c1f8ce 100644 --- a/doc/docs/guides/remote-actions/rate-limiting.md +++ b/doc/docs/guides/remote-actions/rate-limiting.md @@ -26,39 +26,12 @@ In Host mode, rate limits are ignored for the local player, since they cannot fl ### Examples #### Harmless Action (No Penalty) -```csharp -public class PlayerSocial : NetworkBehaviour -{ - // Allows 1 emote every 2 seconds, but the player can burst up to 3 emotes consecutively. - // Spamming emotes too fast will drop the call and apply a penalty to their error limit. - // Penalty set to 0 to not penalize the player for sending too many emotes, we can just ignore them. - [ServerRpc] - [RateLimit(Interval = 2f, Refill = 1, MaxTokens = 3, Penalty = 0)] - public void CmdSendEmote(int emoteId) - { - // ... process and broadcast emote to other players ... - } -} -``` +{{{ Path:'Snippets/RemoteActions/RateLimitingExamples.cs' Name:'rate-limiting-emote' }}} #### Malicious Spam (High Penalty) Hackers or scripts sometimes flood servers with positional updates or action requests. Using a `Penalty` parameter, we can rapidly trigger a global server disconnect. -```csharp -public class PlayerRTSController : NetworkBehaviour -{ - // Players should realistically only be issuing a few move commands a second. - // We allow a healthy burst of 10 commands if they are furiously clicking across the map. - // However, if a cheat sends 100 move instructions instantly, the large Penalty (10) - // will quickly exceed the global error threshold and kick them. - [ServerRpc] - [RateLimit(Interval = 1f, Refill = 5, MaxTokens = 10, Penalty = 10)] - public void CmdMoveUnit(NetworkIdentity unit, Vector3 targetPosition) - { - // ... process movement command ... - } -} -``` +{{{ Path:'Snippets/RemoteActions/RateLimitingExamples.cs' Name:'rate-limiting-move' }}} ## Configuration diff --git a/doc/docs/guides/remote-actions/rpc-examples.md b/doc/docs/guides/remote-actions/rpc-examples.md index 548a4c7b5bf..1eac6c1e0d0 100644 --- a/doc/docs/guides/remote-actions/rpc-examples.md +++ b/doc/docs/guides/remote-actions/rpc-examples.md @@ -9,19 +9,7 @@ Examples of RPC and generated code. Set a player's name from a client and have it synced to other players. -```cs -public class Player : NetworkBehaviour -{ - [SyncVar] - public string PlayerName { get; set; } - - [ServerRpc] - public void RpcChangeName(string newName) - { - PlayerName = newName; - } -} -``` +{{{ Path:'Snippets/RemoteActions/RpcExampleChangeName.cs' Name:'rpc-example-change-name' }}} ### Generated code @@ -29,47 +17,4 @@ Weaver moves the user code into a new function and then replaces the body of the RPCs are registered using the classes static constructor with methods that will read all the parameters and then invoke the user code method. -```cs -public class Player : NetworkBehaviour -{ - [SyncVar] - public string PlayerName { get; set; } - - [ServerRpc] - public void RpcChangeName(string newName) - { - if (this.IsServer) - { - UserCode_RpcChangeName_123456789(newName); - } - else - { - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - writer.WriteString(newName); - ServerRpcSender.Send(this, 123456789, writer, 0, true); - } - } - } - - public void UserCode_RpcChangeName_123456789(string newName) - { - PlayerName = newName; - } - - protected void Skeleton_RpcChangeName_123456789(NetworkReader reader, INetworkPlayer senderConnection, int replyId) - { - this.UserCode_RpcChangeName_123456789(reader.ReadString()); - } - - public Player() - { - this.remoteCallCollection.Register(0, typeof(Player), "Player.RpcChangeName", RpcInvokeType.ServerRpc, new CmdDelegate(Skeleton_RpcChangeName), true); - } - - protected override int GetRpcCount() - { - return 1; - } -} -``` \ No newline at end of file +{{{ Path:'Snippets/RemoteActions/RpcExampleChangeNameGenerated.cs' Name:'rpc-example-change-name-generated' }}} \ No newline at end of file diff --git a/doc/docs/guides/remote-actions/server-rpc.md b/doc/docs/guides/remote-actions/server-rpc.md index 40ff3596734..3a49d501349 100644 --- a/doc/docs/guides/remote-actions/server-rpc.md +++ b/doc/docs/guides/remote-actions/server-rpc.md @@ -9,38 +9,7 @@ To make a function into a Server RPC call, add the [ServerRpc] custom attribute Server RPC Calls functions cannot be static. -``` cs -public class Player : NetworkBehaviour -{ - // Assigned in inspector - public GameObject cubePrefab; - - private void Update() - { - if (!IsLocalPlayer) - { - return; - } - - if (Input.GetKey(KeyCode.X)) - { - DropCube(); - } - } - - [ServerRpc] - private void DropCube() - { - if (cubePrefab != null) - { - Vector3 spawnPos = transform.position + transform.forward * 2; - Quaternion spawnRot = transform.rotation; - GameObject cube = Instantiate(cubePrefab, spawnPos, spawnRot); - NetworkServer.Spawn(cube); - } - } -} -``` +{{{ Path:'Snippets/RemoteActions/ServerRpcDropCube.cs' Name:'server-rpc-drop-cube' }}} :::caution Be careful of sending ServerRpcs from the client every frame! This can cause a lot of network traffic. @@ -51,7 +20,7 @@ Be careful of sending ServerRpcs from the client every frame! This can cause a l ServerRpcs can return values. It can take a long time for the server to reply, so they must return a UniTask which the client can await. To return a value, add a return value using `UniTask` where `MyReturnType` is any [supported Mirage type](/docs/guides/serialization/data-types). In the server, you can make your method async, or you can use `UniTask.FromResult(myResult);`. For example: -{{{ Path:'Snippets/RpcReply.cs' Name:'server-rpc-reply' }}} +{{{ Path:'Snippets/Rpc/RpcReply.cs' Name:'server-rpc-reply' }}} ### ServerRpc and Authority @@ -65,27 +34,7 @@ It is possible to invoke ServerRpcs on non-character objects if any of the follo Server RPC Calls sent from these objects are run on the server instance of the object, not on the associated character object for the client. -```cs -public enum DoorState : byte -{ - Open, Closed -} - -public class Door : NetworkBehaviour -{ - [SyncVar] - public DoorState doorState { get; set; } - - [ServerRpc(requireAuthority = false)] - public void CmdSetDoorState(DoorState newDoorState, INetworkPlayer sender = null) - { - if (sender.identity.GetComponent().hasDoorKey) - { - doorState = newDoorState; - } - } -} -``` +{{{ Path:'Snippets/RemoteActions/ServerRpcDoor.cs' Name:'server-rpc-door' }}} ## Protecting Against Memory Allocation Attacks (MaxLength Attribute) @@ -118,4 +67,3 @@ public void CmdSendInventory([MaxLength(100)] int[] itemIds) - When a `SerializationLimitException` is thrown, the server catches it in the RPC/message handler. - The connection is flagged with `PlayerErrorFlags.SerializationLimit`. - A penalty cost of `100` is applied to the player's error rate limit budget. By default, exceeding the budget triggers an automatic disconnection, disconnecting the malicious client. - diff --git a/doc/docs/guides/scene-loading/network-scene-loader.md b/doc/docs/guides/scene-loading/network-scene-loader.md index b884669a047..caefa2265dc 100644 --- a/doc/docs/guides/scene-loading/network-scene-loader.md +++ b/doc/docs/guides/scene-loading/network-scene-loader.md @@ -27,17 +27,7 @@ These references will be auto-found via `OnValidate` if they are on the same Gam To load a new scene for all connected players: -```cs -public class MyGameManager : MonoBehaviour -{ - public NetworkSceneLoader SceneLoader; - - public void StartGame() - { - SceneLoader.ServerLoadScene("BattleMap").Forget(); - } -} -``` +{{{ Path:'Snippets/SceneLoading/LoaderUsage.cs' Name:'loader-usage' }}} ## How It Works @@ -84,29 +74,5 @@ For games where all players must start at the exact same time (e.g., a competiti Instead of immediately spawning a character when *one* player is ready, check if *all* players are ready first: -```cs -// Modify this inside your duplicated class -private void HandleSceneReadyMessage(INetworkPlayer player, SceneReadyMessage message) -{ - player.SceneIsReady = true; - - // Check if everyone has finished loading - bool allReady = true; - foreach (var p in Server.AllPlayers) - { - if (!p.SceneIsReady) - { - allReady = false; - break; - } - } - - // Only spawn characters once everyone is fully loaded - if (allReady) - { - foreach (var p in Server.AllPlayers) - SpawnCharacterForPlayer(p); - } -} -``` +{{{ Path:'Snippets/SceneLoading/FixedMatchSize.cs' Name:'fixed-match-size' }}} diff --git a/doc/docs/guides/scene-loading/network-scene-manager.md b/doc/docs/guides/scene-loading/network-scene-manager.md index 36bed9a085b..56dda8df839 100644 --- a/doc/docs/guides/scene-loading/network-scene-manager.md +++ b/doc/docs/guides/scene-loading/network-scene-manager.md @@ -20,89 +20,49 @@ the network scene manager. This will load up a new scene on the server and tell all current player's loaded on the server to load the scene up. -```cs -public class LoadScene : MonoBehaviour -{ - public void Start() - { - NetworkSceneManager sceneManager = GetComponent(); - - sceneManager.ServerLoadSceneNormal("path to scene asset file.") - } -} -``` +{{{ Path:'Snippets/SceneLoading/LoadSceneNormal.cs' Name:'load-scene-normal' }}} :::note If you require physics scenes to load up on the server you can override the default parameter like so. ::: -```cs -sceneManager.ServerLoadSceneNormal("path to scene asset file.", new LoadSceneParameters { loadSceneMode = LoadSceneMode.Normal, localPhysicsMode = LocalPhysicsMode.Physics2D }); -``` +{{{ Path:'Snippets/SceneLoading/LoadSceneNormalParams.cs' Name:'load-scene-normal-params' }}} ## Load Scene Additively This will load a scene additively on the server and tell specific clients to do the same. Example shows send to everyone. -```cs -public class LoadSceneAdditively : MonoBehaviour -{ - public void Start() - { - NetworkSceneManager sceneManager = GetComponent(); - - sceneManager.ServerLoadSceneAdditively("path to scene asset file.", sceneManager.Server.Players) - } -} -``` +{{{ Path:'Snippets/SceneLoading/LoadSceneAdditively.cs' Name:'load-scene-additively' }}} :::note If you want to send the additive scene to only specific players we can do it like so. You must get the player on your own. ::: -```cs -sceneManager.ServerLoadSceneAdditively("path to scene asset file.", Player) -``` +{{{ Path:'Snippets/SceneLoading/LoadSceneAdditivelyPlayer.cs' Name:'load-scene-additively-player' }}} :::note Also if you want to load the scene normally to specific players versus additively like the server you can override the parameter to do so also. The server will still load additively, the reason is if you need fully normal loading you can use the above method instead to do it. ::: -```cs -sceneManager.ServerLoadSceneAdditively("path to scene asset file.", Player, true) -``` +{{{ Path:'Snippets/SceneLoading/LoadSceneAdditivelyPlayer.cs' Name:'load-scene-additively-player-normal' }}} :::note Also if you want to load the scene in physic's mode you can override another parameter also to do so. You can also make clients load normally in the example below we keep it false to load the client side additively too. ::: -```cs -sceneManager.ServerLoadSceneAdditively("path to scene asset file.", Player, false, new LoadSceneParameters { loadSceneMode = LoadSceneMode.Additively, localPhysicsMode = LocalPhysicsMode.Physics2D ) -``` +{{{ Path:'Snippets/SceneLoading/LoadSceneAdditivelyPlayer.cs' Name:'load-scene-additively-player-physics' }}} This will unload a scene additively on the server and tell specific clients to do the same. Example shows send to everyone. -```cs -public class UnLoadSceneAdditively : MonoBehaviour -{ - public void Start() - { - NetworkSceneManager sceneManager = GetComponent(); - - sceneManager.ServerUnloadSceneAdditively("path to scene asset file.", sceneManager.Server.Players) - } -} -``` +{{{ Path:'Snippets/SceneLoading/UnLoadSceneAdditively.cs' Name:'unload-scene-additively' }}} :::note If you want to send the additive scene to only specific players we can do it like so. You must get the player on your own. ::: -```cs -sceneManager.ServerLoadSceneAdditively("path to scene asset file.", Player) -``` +{{{ Path:'Snippets/SceneLoading/LoadSceneAdditivelyPlayer.cs' Name:'load-scene-additively-player' }}} ## Virtual Methods @@ -120,49 +80,15 @@ Some of the methods in NetworkSceneManager can be overridden to customize how it By default OnServerAuthenticated sends the active scene and all additive scenes to the client, It can be overridden to only send the active scene: -```cs -public class MySceneManager : NetworkSceneManager -{ - protected internal override void OnServerAuthenticated(INetworkPlayer player) - { - // just load server's active scene instead of all additive scenes as well - player.Send(new SceneMessage { MainActivateScene = ActiveScenePath }); - player.Send(new SceneReadyMessage()); - } -} -``` +{{{ Path:'Snippets/SceneLoading/CustomSceneManager.cs' Name:'custom-scene-manager-on-server-authenticated' }}} ### Example - Start By default, `Start` registers all our listeners for scene management handling. If you need to override it then do this and add your stuff. -```cs -public class MySceneManager : NetworkSceneManager -{ - protected internal override void Start() - { - // add your stuff before. - - base.Start(); - - // add your stuff after. - } -} -``` +{{{ Path:'Snippets/SceneLoading/CustomSceneManager.cs' Name:'custom-scene-manager-start' }}} ### Example - OnDestroy By default OnDestroy de-registers all our listener's for scene management handling. If you need to override it then do this and add your stuff. -```cs -public class MySceneManager : NetworkSceneManager -{ - protected internal override void OnDestroy() - { - // add your stuff before. - - base.OnDestroy(); - - // add your stuff after. - } -} -``` +{{{ Path:'Snippets/SceneLoading/CustomSceneManager.cs' Name:'custom-scene-manager-on-destroy' }}} diff --git a/doc/docs/guides/serialization/advanced.md b/doc/docs/guides/serialization/advanced.md index c95c7584c86..9486bccbcc8 100644 --- a/doc/docs/guides/serialization/advanced.md +++ b/doc/docs/guides/serialization/advanced.md @@ -87,14 +87,7 @@ The weaver does not check properties Weaver will use the underlying type of an enum to read and write them. By default this is `int`. For example, `Switch` will use the `byte` read/write functions to be serialized -```cs -public enum Switch : byte -{ - Left, - Middle, - Right, -} -``` +{{{ Path:'Snippets/Serialization/SwitchEnum.cs' Name:'switch-enum' }}} #### Collections @@ -105,28 +98,12 @@ be a supported type or have a custom read/write function. For example: - `float[]` is a supported type because Mirage has a built-in read/write function for `float`. - `MyData[]` is a supported type as Weaver is able to generate a read/write function for `MyData` -```cs -public struct MyData -{ - public int someValue; - public float anotherValue; -} -``` +{{{ Path:'Snippets/Serialization/MyDataStruct.cs' Name:'my-data' }}} ## Adding Custom Read Write functions Custom read/write functions are static methods like this: -```cs -public static void WriteMyType(this NetworkWriter writer, MyType value) -{ - // write MyType data here -} - -public static MyType ReadMyType(this NetworkReader reader) -{ - // read MyType data here -} -``` +{{{ Path:'Snippets/Serialization/CustomReadWrite.cs' Name:'custom-read-write' }}} It is best practice to make read/write [extension methods](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods) so they can be called like `writer.WriteMyType(value)`. @@ -138,95 +115,17 @@ Weaver won't write properties, but a custom writer can be used to send them over This can be useful if you want to have `private set` for your properties -```cs -public struct MyData -{ - public int someValue { get; private set; } - public float anotherValue { get; private set; } - - public MyData(int someValue, float anotherValue) - { - this.someValue = someValue; - this.anotherValue = anotherValue; - } -} - -public static class CustomReadWriteFunctions -{ - public static void WriteMyType(this NetworkWriter writer, MyData value) - { - writer.WriteInt32(value.someValue); - writer.WriteSingle(value.anotherValue); - } - - public static MyData ReadMyType(this NetworkReader reader) - { - return new MyData(reader.ReadInt32(), reader.ReadSingle()); - } -} -``` +{{{ Path:'Snippets/Serialization/PropertiesExample.cs' Name:'properties-example' }}} #### Unsupported type Example Rigidbody is an unsupported type because it inherits from `Component`. But a custom writer can be added so that it is synced using a NetworkIdentity if one is attached. -```cs -public struct MyCollision -{ - public Vector3 force; - public Rigidbody rigidbody; -} - -public static class CustomReadWriteFunctions -{ - public static void WriteMyCollision(this NetworkWriter writer, MyCollision value) - { - writer.WriteVector3(value.force); - - NetworkIdentity networkIdentity = value.rigidbody.GetComponent(); - writer.WriteNetworkIdentity(networkIdentity); - } - - public static MyCollision ReadMyCollision(this NetworkReader reader) - { - Vector3 force = reader.ReadVector3(); - - NetworkIdentity networkIdentity = reader.ReadNetworkIdentity(); - Rigidbody rigidBody = networkIdentity != null - ? networkIdentity.GetComponent() - : null; - - return new MyCollision - { - force = force, - rigidbody = rigidBody, - }; - } -} -``` +{{{ Path:'Snippets/Serialization/UnsupportedTypeExample.cs' Name:'collision-example' }}} Above are functions for `MyCollision`, but instead, you could add functions for `Rigidbody` and let weaver would generate a writer for `MyCollision`. -```cs -public static class CustomReadWriteFunctions -{ - public static void WriteRigidbody(this NetworkWriter writer, Rigidbody rigidbody) - { - NetworkIdentity networkIdentity = rigidbody.GetComponent(); - writer.WriteNetworkIdentity(networkIdentity); - } - - public static Rigidbody ReadRigidbody(this NetworkReader reader) - { - NetworkIdentity networkIdentity = reader.ReadNetworkIdentity(); - Rigidbody rigidBody = networkIdentity != null - ? networkIdentity.GetComponent() - : null; - - return rigidBody; - } -} -``` +{{{ Path:'Snippets/Serialization/UnsupportedTypeExample.cs' Name:'rigidbody-example' }}} ### Supporting MaxLength in Custom Functions diff --git a/doc/docs/guides/serialization/data-types.md b/doc/docs/guides/serialization/data-types.md index ea987ea2265..676bff81ced 100644 --- a/doc/docs/guides/serialization/data-types.md +++ b/doc/docs/guides/serialization/data-types.md @@ -37,30 +37,7 @@ It could also be null because the game object is excluded from a client due to n You may find that it's more robust to sync the `NetworkIdentity.NetID` (`uint`) instead, and do your own lookup in `NetworkIdentity.World.Spawned` to get the object, perhaps in a coroutine: -```cs - public GameObject target; - - [SyncVar(hook = nameof(OnTargetChanged))] - public uint targetID { get; set; } - - void OnTargetChanged(uint _, uint newValue) - { - if (NetworkIdentity.World.Spawned.TryGetValue(targetID, out NetworkIdentity identity)) - target = identity.gameObject; - else - StartCoroutine(SetTarget()); - } - - IEnumerator SetTarget() - { - while (target == null) - { - yield return null; - if (NetworkIdentity.World.SpawnedObjects.TryGetValue(targetID, out NetworkIdentity identity)) - target = identity.gameObject; - } - } -``` +{{{ Path:'Snippets/Serialization/DataTypesSnippets.cs' Name:'game-object-lookup' }}} ## Custom Data Types @@ -70,20 +47,7 @@ Sometimes you may want to serialize data that uses a different type not supporte You can add support for any type by adding extension methods to `NetworkWriter` and `NetworkReader`. For example, to add support for `DateTime`, add this somewhere in your project: -```cs -public static class DateTimeReaderWriter -{ - public static void WriteDateTime(this NetworkWriter writer, DateTime dateTime) - { - writer.WriteInt64(dateTime.Ticks); - } - - public static DateTime ReadDateTime(this NetworkReader reader) - { - return new DateTime(reader.ReadInt64()); - } -} -``` +{{{ Path:'Snippets/Serialization/DataTypesSnippets.cs' Name:'datetime-serializer' }}} ...then you can use `DateTime` in your `[ServerRpc]` or `SyncList` @@ -95,100 +59,11 @@ Sometimes you might want to send a polymorphic data type to your commands. Mirag This code does not work out of the box. ::: -```cs -class Item -{ - public string name; -} - -class Weapon : Item -{ - public int hitPoints; -} - -class Armor : Item -{ - public int hitPoints; - public int level; -} - -class Player : NetworkBehaviour -{ - [ServerRpc] - void ServerRpcEquip(Item item) - { - // IMPORTANT: this does not work. Mirage will pass you an object of type item - // even if you pass a weapon or an armor. - if (item is Weapon weapon) - { - // The item is a weapon, - // maybe you need to equip it in the hand - } - else if (item is Armor armor) - { - // you might want to equip armor in the body - } - } - - [ServerRpc] - void ServerEquipArmor(Armor armor) - { - // IMPORTANT: this does not work either, you will receive an armor, but - // the armor will not have a valid Item.name, even if you passed an armor with name - } -} -``` +{{{ Path:'Snippets/Serialization/DataTypesSnippets.cs' Name:'polymorphic-equip' }}} `ServerRpcEquip` will work if you provide a custom serializer for the `Item` type. For example: -```cs - -public static class ItemSerializer -{ - const byte WEAPON = 1; - const byte ARMOR = 2; - - public static void WriteItem(this NetworkWriter writer, Item item) - { - if (item is Weapon weapon) - { - writer.WriteByte(WEAPON); - writer.WriteString(weapon.name); - writer.WritePackedInt32(weapon.hitPoints); - } - else if (item is Armor armor) - { - writer.WriteByte(ARMOR); - writer.WriteString(armor.name); - writer.WritePackedInt32(armor.hitPoints); - writer.WritePackedInt32(armor.level); - } - } - - public static Item ReadItem(this NetworkReader reader) - { - byte type = reader.ReadByte(); - switch(type) - { - case WEAPON: - return new Weapon - { - name = reader.ReadString(), - hitPoints = reader.ReadPackedInt32() - }; - case ARMOR: - return new Armor - { - name = reader.ReadString(), - hitPoints = reader.ReadPackedInt32(), - level = reader.ReadPackedInt32() - }; - default: - throw new Exception($"Invalid weapon type {type}"); - } - } -} -``` +{{{ Path:'Snippets/Serialization/DataTypesSnippets.cs' Name:'polymorphic-serializer' }}} ## Scriptable Objects @@ -199,30 +74,5 @@ However, the generated reader and writer are not suitable for every occasion. Sc Instead of passing the scriptable object data, you can pass the name and the other side can look up the same object by name. This way you can have any kind of data in your scriptable object. You can do that by providing a custom reader and writer. Here is an example: -```cs -[CreateAssetMenu(fileName = "New Armor", menuName = "Armor Data")] -class Armor : ScriptableObject -{ - public int Hitpoints; - public int Weight; - public string Description; - public Texture2D Icon; - // ... -} - -public static class ArmorSerializer -{ - public static void WriteArmor(this NetworkWriter writer, Armor armor) - { - // No need to serialize the data, just the name of the armor. - writer.WriteString(armor.name); - } - - public static Armor ReadArmor(this NetworkReader reader) - { - // Load the same armor by name. The data will come from the asset in Resources folder. - return Resources.Load(reader.ReadString()); - } -} -``` +{{{ Path:'Snippets/Serialization/DataTypesSnippets.cs' Name:'scriptable-object-serializer' }}} diff --git a/doc/docs/guides/serialization/generics.md b/doc/docs/guides/serialization/generics.md index 0f55057a187..817df4f3aec 100644 --- a/doc/docs/guides/serialization/generics.md +++ b/doc/docs/guides/serialization/generics.md @@ -9,18 +9,7 @@ Mirage supports generic types for [SyncVar](/docs/guides/sync/sync-var), [Rpcs]( By making a [NetworkBehaviour](/docs/guides/game-objects/network-behaviour) generic you can then use generic SyncVar fields or use the generic in an RPC. -```cs -public class MyGenericBehaviour : NetworkBehaviour -{ - [SyncVar] - public T Value { get; set; } - - public void MyRpc(T value) - { - // do stuff - } -} -``` +{{{ Path:'Snippets/Serialization/GenericsSnippets.cs' Name:'generic-behaviour' }}} :::warning Making the RPC itself generic does not work. For example, `MyRpc(T value)` will not work. This is because the receiver will have no idea what generic to invoke the type as. @@ -32,30 +21,11 @@ For a type to work as a generic, it must have a write and read that Mirage can f For custom types Mirage will try to automatically find them and generate functions, however, this does not always work. Adding `[NetworkMessage]` to the type will tell Mirage to generate functions for it. -```cs -[NetworkMessage] -public struct MyCustomType -{ - public int Value; -} -``` +{{{ Path:'Snippets/Serialization/GenericsSnippets.cs' Name:'custom-type' }}} Alternatively, you can manually create Write and Read functions for your type -```cs -public static class MyCustomTypeExtensions -{ - public static void Write(this NetworkWriter writer, MyCustomType value) - { - // write here - } - - public static MyCustomType Read(this NetworkReader reader) - { - // read here - } -} -``` +{{{ Path:'Snippets/Serialization/GenericsSnippets.cs' Name:'custom-type-extensions' }}} ## Network Messages and other types @@ -63,25 +33,7 @@ Generic messages are partly supported. Generic instances can be used as messages This also includes using generic types in RPC or inside other types as long they are generic instances. -```cs -public struct MyMessage -{ - public T Value; -} - -class Manager -{ - void Start() - { - Server.MessageHandler.RegisterHandler>(HandleMessage); - } - - void HandleIntMessage(INetworkPlayer player, MyMessage msg) - { - // do stuff - } -} -``` +{{{ Path:'Snippets/Serialization/GenericsSnippets.cs' Name:'generic-message' }}} :::note Generic message should not have `[NetworkMessage]` because this cause Mirage to try to make a writer for the generic itself. Only generic instances (eg `MyMessage`) can have serialize functions @@ -91,15 +43,4 @@ Generic message should not have `[NetworkMessage]` because this cause Mirage to SyncList, SyncDictionary, and SyncSet can have generic types as their element type as long as it is a generic instance (eg `MyType` not `MyType`). -```cs -public struct MyType -{ - public bool Option; - public T Value; -} - -public class MyBehaviour : NetworkBehaviour -{ - public SyncList> myList; -} -``` \ No newline at end of file +{{{ Path:'Snippets/Serialization/GenericsSnippets.cs' Name:'generic-collections' }}} \ No newline at end of file diff --git a/doc/docs/guides/serialization/string-store.md b/doc/docs/guides/serialization/string-store.md index 64f96b744c9..5b4939c2048 100644 --- a/doc/docs/guides/serialization/string-store.md +++ b/doc/docs/guides/serialization/string-store.md @@ -29,50 +29,7 @@ You will then need to serialize the `StringStore` separately from your message a Using `NetworkWriterPool` and `NetworkReaderPool` is the most efficient way to handle the temporary buffers required for `StringStore`. -```csharp -public static void WriteMission(this NetworkWriter finalWriter, Mission mission) -{ - // Get a temporary writer from the pool - using (PooledNetworkWriter innerWriter = NetworkWriterPool.GetWriter()) - { - // Create a new store and attach it to the temporary writer - StringStore stringStore = new StringStore(); - innerWriter.StringStore = stringStore; - - // Write the mission data. - // Any repeated strings (like Objective titles or NPC names) - // will be indexed in 'stringStore'. - mission.OnSerialize(innerWriter); - - // Write the populated store to the REAL writer first - finalWriter.WriteStringStore(stringStore); - - // Write the actual message data as a segment - finalWriter.WriteBytesAndSizeSegment(innerWriter.ToArraySegment()); - } -} - -public static Mission ReadMission(this NetworkReader finalReader) -{ - // Read the StringStore that was sent first - StringStore stringStore = finalReader.ReadStringStore(); - - // Read the data segment containing the mission - ArraySegment segment = finalReader.ReadBytesAndSizeSegment(); - - // Get a pooled reader for the segment and attach the store - using (PooledNetworkReader innerReader = NetworkReaderPool.GetReader(segment, null)) - { - innerReader.StringStore = stringStore; - - // 4. Deserialize the mission. - // ReadString calls will now correctly resolve indices using the store. - var mission = new Mission(); - mission.OnDeserialize(innerReader); - return mission; - } -} -``` +{{{ Path:'Snippets/Serialization/StringStoreSnippets.cs' Name:'mission-example' }}} ## Brotli Compression (Advanced) @@ -87,53 +44,4 @@ The `StringStoreBrotliEncoder` handles the compression and prepares raw payloads Because Brotli compression is CPU-intensive, you should **Encode once and reuse** the encoder for all players, including those who join the session late. -```csharp -// SERVER: Compressing and caching -public class WorldServer : MonoBehaviour -{ - // keep the StringStoreBrotliEncoder (and its results) so that it can be sent to new players - // this is to avoid heavy cpu encoding every time a new player joins - private StringStoreBrotliEncoder _worldEncoder; - - public void InitializeWorld(WorldData world) - { - StringStore store = new StringStore(); - // ... populate store by writing world data to a temp writer ... - - // Create the encoder once. This performs the heavy compression logic. - _worldEncoder = StringStoreBrotliEncoder.Encode(store); - } - - public void OnPlayerJoin(INetworkPlayer player) - { - // Send the pre-compressed payloads to the player. - // This is very fast as it just sends cached byte segments. - _worldEncoder.Send(player); - } -} - -// CLIENT: Receiving -public class MissionManager : MonoBehaviour -{ - private StringStoreBrotliDecoder _decoder; - - public void Start() - { - // Initialize the decoder with the Network Client. - // NOTE: StringStoreBrotliDecoder will only receive 1 set of messages, it will unregister the message handlers after it as received one - _decoder = new StringStoreBrotliDecoder(Client.Instance); - - // Subscribe to the completion event - _decoder.OnReceived += () => - { - Debug.Log("Strings Received! Ready to deserialize mission."); - ProcessMission(_decoder.StringStore); - }; - } - - private void ProcessMission(StringStore store) - { - // Use the store in your Readers as shown in the previous example - } -} -``` +{{{ Path:'Snippets/Serialization/StringStoreSnippets.cs' Name:'brotli-example' }}} diff --git a/doc/docs/guides/serialization/sync-prefab.md b/doc/docs/guides/serialization/sync-prefab.md index eb846bb790c..0aa3cb982ce 100644 --- a/doc/docs/guides/serialization/sync-prefab.md +++ b/doc/docs/guides/serialization/sync-prefab.md @@ -24,4 +24,4 @@ On the client side, the `RpcShoot` method finds the prefab from `ClientObjectMan Add `[NetworkedPrefab]` attribute to your inspector field to show if it is set up correctly. ::: -{{{ Path:'Snippets/UsingSyncPrefab.cs' Name:'shoot' }}} +{{{ Path:'Snippets/Serialization/UsingSyncPrefab.cs' Name:'shoot' }}} diff --git a/doc/docs/guides/sync/code-generation.md b/doc/docs/guides/sync/code-generation.md index 99777051a52..697cbaa9c43 100644 --- a/doc/docs/guides/sync/code-generation.md +++ b/doc/docs/guides/sync/code-generation.md @@ -11,121 +11,15 @@ The compiler also generates `SerializeSyncVars` and `DeserializeSyncVars` method So for this script: -```cs -using Mirage; - -public class Data : NetworkBehaviour -{ - [SyncVar(hook = nameof(OnInt1Changed))] - public int int1 { get; set; } = 66; - - [SyncVar] - public int int2 { get; set; } = 23487; - - [SyncVar] - public string MyString { get; set; } = "Example string"; - - void OnInt1Changed(int oldValue, int newValue) - { - // do something here - } -} -``` +{{{ Path:'Snippets/Sync/CodeGeneration.cs' Name:'CodeGenerationExample' }}} The following sample shows the code that is generated by Mirage for the `SerializeSyncVars` function which is called inside [NetworkBehaviour](/docs/reference/Mirage/NetworkBehaviour)`.OnSerialize`: -```cs -public override bool SerializeSyncVars(NetworkWriter writer, bool initialState) -{ - // Write any SyncVars in base class - bool written = base.SerializeSyncVars(writer, forceAll); - - if (initialState) - { - // The first time a game object is sent to a client, send all the data (and no dirty bits) - writer.WritePackedUInt32((uint)this.int1); - writer.WritePackedUInt32((uint)this.int2); - writer.Write(this.MyString); - return true; - } - else - { - // Writes which SyncVars have changed - writer.WritePackedUInt64(base.syncVarDirtyBits); - - if ((base.get_syncVarDirtyBits() & 1u) != 0u) - { - writer.WritePackedUInt32((uint)this.int1); - written = true; - } - - if ((base.get_syncVarDirtyBits() & 2u) != 0u) - { - writer.WritePackedUInt32((uint)this.int2); - written = true; - } - - if ((base.get_syncVarDirtyBits() & 4u) != 0u) - { - writer.Write(this.MyString); - written = true; - } - - return written; - } -} -``` +{{{ Path:'Snippets/Sync/CodeGeneration.cs' Name:'SerializeSyncVarsExample' }}} The following sample shows the code that is generated by Mirage for the `DeserializeSyncVars` function which is called inside [NetworkBehaviour](/docs/reference/Mirage/NetworkBehaviour)`.OnDeserialize`: -```cs -public override void DeserializeSyncVars(NetworkReader reader, bool initialState) -{ - // Read any SyncVars in base class - base.DeserializeSyncVars(reader, initialState); - - if (initialState) - { - // The first time a game object is sent to a client, read all the data (and no dirty bits) - int oldInt1 = this.int1; - this.int1 = (int)reader.ReadPackedUInt32(); - // if old and new values are not equal, call hook - if (!base.SyncVarEqual(num, ref this.int1)) - { - this.OnInt1Changed(num, this.int1); - } - - this.int2 = (int)reader.ReadPackedUInt32(); - this.MyString = reader.ReadString(); - return; - } - - int dirtySyncVars = (int)reader.ReadPackedUInt32(); - // is 1st SyncVar dirty - if ((dirtySyncVars & 1) != 0) - { - int oldInt1 = this.int1; - this.int1 = (int)reader.ReadPackedUInt32(); - // if old and new values are not equal, call hook - if (!base.SyncVarEqual(num, ref this.int1)) - { - this.OnInt1Changed(num, this.int1); - } - } - - // is 2nd SyncVar dirty - if ((dirtySyncVars & 2) != 0) - { - this.int2 = (int)reader.ReadPackedUInt32(); - } - - // is 3rd SyncVar dirty - if ((dirtySyncVars & 4) != 0) - { - this.MyString = reader.ReadString(); - } -} -``` +{{{ Path:'Snippets/Sync/CodeGeneration.cs' Name:'DeserializeSyncVarsExample' }}} If a [NetworkBehaviour](/docs/reference/Mirage/NetworkBehaviour) has a base class that also has serialization functions, the base class functions should also be called. diff --git a/doc/docs/guides/sync/custom-serialization.md b/doc/docs/guides/sync/custom-serialization.md index 6e97c583163..3451f22dee3 100644 --- a/doc/docs/guides/sync/custom-serialization.md +++ b/doc/docs/guides/sync/custom-serialization.md @@ -9,13 +9,9 @@ In most cases, the use of SyncVars is enough for your game scripts to serialize To perform your own custom serialization, you can implement virtual functions on [NetworkBehaviour](/docs/reference/Mirage/NetworkBehaviour) to be used for SyncVar serialization. These functions are: -```cs -public virtual bool OnSerialize(NetworkWriter writer, bool initialState); -``` +{{{ Path:'Snippets/Sync/CustomSerialization.cs' Name:'OnSerializeSignature' }}} -```cs -public virtual void OnDeserialize(NetworkReader reader, bool initialState); -``` +{{{ Path:'Snippets/Sync/CustomSerialization.cs' Name:'OnDeserializeSignature' }}} Use the `initialState` flag to differentiate between the first time a game object is serialized and when incremental updates can be sent. The first time a game object is sent to a client, it must include a full state snapshot, but subsequent updates can save on bandwidth by including only incremental changes. diff --git a/doc/docs/guides/sync/sync-objects/sync-dictionary.md b/doc/docs/guides/sync/sync-objects/sync-dictionary.md index bc2681ea2bf..8b6092701a1 100644 --- a/doc/docs/guides/sync/sync-objects/sync-dictionary.md +++ b/doc/docs/guides/sync/sync-objects/sync-dictionary.md @@ -14,37 +14,7 @@ You need to initialize the SyncDictionary immediately after the definition for t ::: ### Basic example -```cs -using UnityEngine; -using Mirage; -using Mirage.Collections; - -[System.Serializable] -public struct Item -{ - public string name; - public int hitPoints; - public int durability; -} - -public class Player : NetworkBehaviour -{ - public readonly SyncDictionary equipment = new SyncDictionary(); - - private void Awake() - { - Identity.OnStartServer.AddListener(OnStartServer); - } - - private void OnStartServer() - { - equipment.Add("head", new Item { name = "Helmet", hitPoints = 10, durability = 20 }); - equipment.Add("body", new Item { name = "Epic Armor", hitPoints = 50, durability = 50 }); - equipment.Add("feet", new Item { name = "Sneakers", hitPoints = 3, durability = 40 }); - equipment.Add("hands", new Item { name = "Sword", hitPoints = 30, durability = 15 }); - } -} -``` +{{{ Path:'Snippets/Sync/SyncDictionaryExamples.cs' Name:'SyncDictionaryBasicExample' }}} ## Callbacks You can detect when a SyncDictionary changes on the client and/or server. This is especially useful for refreshing your UI, character appearance, etc. @@ -61,42 +31,8 @@ By the time you subscribe, the dictionary will already be initialized, so you wi ::: ### Example -```cs -using Mirage; -using Mirage.Collections; - -public class Player : NetworkBehaviour -{ - public readonly SyncDictionary equipment = new SyncDictionary(); - public readonly SyncDictionary hotbar = new SyncDictionary(); - - // This will hook the callback on both server and client - private void Awake() - { - equipment.OnChange += UpdateEquipment; - Identity.OnStartClient.AddListener(OnStartClient); - } - - // Hotbar changes will only be invoked on clients - private void OnStartClient() - { - hotbar.OnChange += UpdateHotbar; - } - - private void UpdateEquipment() - { - // Here you can refresh your UI for instance - } - - private void UpdateHotbar() - { - // Here you can refresh your UI for instance - } -} -``` +{{{ Path:'Snippets/Sync/SyncDictionaryExamples.cs' Name:'SyncDictionaryCallbackExample' }}} By default, `SyncDictionary` uses a [`Dictionary`](https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2?view=netstandard-2.0) to store its data. If you want to use a different dictionary implementation, add a constructor and pass the dictionary implementation to the parent constructor. For example: -```cs -public SyncDictionary myDict = new SyncIDictionary(new MyDictionary()); -``` \ No newline at end of file +{{{ Path:'Snippets/Sync/SyncDictionaryExamples.cs' Name:'SyncDictionaryCustomImplementation' }}} \ No newline at end of file diff --git a/doc/docs/guides/sync/sync-objects/sync-hash-set.md b/doc/docs/guides/sync/sync-objects/sync-hash-set.md index dd93a8a7b8e..d5f378f3c13 100644 --- a/doc/docs/guides/sync/sync-objects/sync-hash-set.md +++ b/doc/docs/guides/sync/sync-objects/sync-hash-set.md @@ -16,29 +16,7 @@ You need to initialize the SyncHashSet immediately after the definition in order ::: ### Basic example -```cs -[System.Serializable] -public class SyncSkillSet : SyncHashSet {} - -public class Player : NetworkBehaviour { - - [SerializeField] - readonly SyncSkillSet skills = new SyncSkillSet(); - - int skillPoints = 10; - - [Command] - public void CmdLearnSkill(string skillName) - { - if (skillPoints > 1) - { - skillPoints--; - - skills.Add(skillName); - } - } -} -``` +{{{ Path:'Snippets/Sync/SyncHashSetExamples.cs' Name:'SyncHashSetBasicExample' }}} # Callbacks You can detect when a SyncHashSet changes on the client and/or the server. This is especially useful for refreshing your UI, character appearance, etc. @@ -49,36 +27,4 @@ Subscribe to the Callback event typically during `Start`, `OnClientStart`, or `O Note that by the time you subscribe, the set will already be initialized, so you will not get a call for the initial data, only updates. ::: -```cs -[System.Serializable] -public class SyncSetBuffs : SyncHashSet {}; - -public class Player : NetworkBehaviour -{ - [SerializeField] - public readonly SyncSetBuffs buffs = new SyncSetBuffs(); - - // this will add the delegate on the client. - // Use OnStartServer instead if you want it on the server - public override void OnStartClient() - { - buffs.Callback += OnBuffsChanged; - } - - private void OnBuffsChanged(SyncSetBuffs.Operation op, string buff) - { - switch (op) - { - case SyncSetBuffs.Operation.OP_ADD: - // we added a buff, draw an icon on the character - break; - case SyncSetBuffs.Operation.OP_CLEAR: - // clear all buffs from the character - break; - case SyncSetBuffs.Operation.OP_REMOVE: - // We removed a buff from the character - break; - } - } -} -``` +{{{ Path:'Snippets/Sync/SyncHashSetExamples.cs' Name:'SyncHashSetCallbackExample' }}} diff --git a/doc/docs/guides/sync/sync-objects/sync-list.md b/doc/docs/guides/sync/sync-objects/sync-list.md index bc7b2d60234..04ae2b6b227 100644 --- a/doc/docs/guides/sync/sync-objects/sync-list.md +++ b/doc/docs/guides/sync/sync-objects/sync-list.md @@ -14,43 +14,7 @@ You need to initialize the SyncList immediately after the definition for them to ::: ### Basic example -```cs -using Mirage; -using Mirage.Collections; - -[System.Serializable] -public struct Item -{ - public string name; - public int amount; - public Color32 color; -} - -public class Player : NetworkBehaviour -{ - private readonly SyncList inventory = new SyncList(); - - public int coins = 100; - - [ServerRpc] - public void Purchase(string itemName) - { - if (coins > 10) - { - coins -= 10; - Item item = new Item - { - name = "Sword", - amount = 3, - color = new Color32(125, 125, 125, 255) - }; - - // During next synchronization, all clients will see the item - inventory.Add(item); - } - } -} -``` +{{{ Path:'Snippets/Sync/SyncListExamples.cs' Name:'SyncListBasicExample' }}} ## Callbacks You can detect when a `SyncList` changes on the client and/or server. This is especially useful for refreshing your UI, character appearance, etc. @@ -67,42 +31,8 @@ By the time you subscribe, the list will already be initialized, so you will not ::: ### Example -```cs -using Mirage; -using Mirage.Collections; - -public class Player : NetworkBehaviour -{ - private readonly SyncList inventory = new SyncList(); - private readonly SyncList hotbar = new SyncList(); - - // This will hook the callback on both server and client - private void Awake() - { - inventory.OnChange += UpdateInventory; - Identity.OnStartClient.AddListener(OnStartClient); - } - - // Hotbar changes will only be invoked on clients - private void OnStartClient() - { - hotbar.OnChange += UpdateHotbar; - } - - private void UpdateInventory() - { - // Here you can refresh your UI for instance - } - - private void UpdateHotbar() - { - // Here you can refresh your UI for instance - } -} -``` +{{{ Path:'Snippets/Sync/SyncListExamples.cs' Name:'SyncListCallbackExample' }}} By default, `SyncList` uses a [`List`](https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1?view=netstandard-2.0) to store its data. If you want to use a different list implementation, add a constructor and pass the list implementation to the parent constructor. For example: -```cs -public SyncList myList = new SyncList(new MyIList()); -``` \ No newline at end of file +{{{ Path:'Snippets/Sync/SyncListExamples.cs' Name:'SyncListCustomImplementation' }}} \ No newline at end of file diff --git a/doc/docs/guides/sync/sync-objects/sync-sorted-set.md b/doc/docs/guides/sync/sync-objects/sync-sorted-set.md index 2c1ea0b915c..e93b2916d1a 100644 --- a/doc/docs/guides/sync/sync-objects/sync-sorted-set.md +++ b/doc/docs/guides/sync/sync-objects/sync-sorted-set.md @@ -17,27 +17,7 @@ Create a class that derives from SyncSortedSet for your specific type. This is n You need to initialize the SyncSortedSet immediately after the definition for them to work. You can mark them as `readonly` to enforce proper usage. ::: -```cs -class Player : NetworkBehaviour { - - class SyncSkillSet : SyncSortedSet {} - - readonly SyncSkillSet skills = new SyncSkillSet(); - - int skillPoints = 10; - - [Command] - public void CmdLearnSkill(string skillName) - { - if (skillPoints > 1) - { - skillPoints--; - - skills.Add(skillName); - } - } -} -``` +{{{ Path:'Snippets/Sync/SyncSortedSetExamples.cs' Name:'SyncSortedSetBasicExample' }}} You can also detect when a SyncSortedSet changes. This is useful for refreshing your character in the client or determining when you need to update your database. Subscribe to the Callback event typically during `Start`, `OnClientStart`, or `OnServerStart` for that. @@ -45,34 +25,4 @@ You can also detect when a SyncSortedSet changes. This is useful for refreshing That by the time you subscribe, the set will already be initialized, so you will not get a call for the initial data, only updates. ::: -```cs -public class Player : NetworkBehaviour -{ - private class SyncSetBuffs : SyncSortedSet {}; - - private readonly SyncSetBuffs buffs = new SyncSetBuffs(); - - // This will add the delegate on the client. - // Use OnStartServer instead if you want it on the server - public override void OnStartClient() - { - buffs.Callback += OnBuffsChanged; - } - - private void OnBuffsChanged(SyncSetBuffs.Operation op, string buff) - { - switch (op) - { - case SyncSetBuffs.Operation.OP_ADD: - // we added a buff, draw an icon on the character - break; - case SyncSetBuffs.Operation.OP_CLEAR: - // clear all buffs from the character - break; - case SyncSetBuffs.Operation.OP_REMOVE: - // We removed a buff from the character - break; - } - } -} -``` +{{{ Path:'Snippets/Sync/SyncSortedSetExamples.cs' Name:'SyncSortedSetCallbackExample' }}} diff --git a/doc/docs/guides/sync/sync-var-hooks.md b/doc/docs/guides/sync/sync-var-hooks.md index 3a446a4f3d8..464aed79d07 100644 --- a/doc/docs/guides/sync/sync-var-hooks.md +++ b/doc/docs/guides/sync/sync-var-hooks.md @@ -7,9 +7,7 @@ sidebar_position: 3 Hooks are set using the `hook` option on the `SyncVar` attribute, the hook needs to be in the same class as the `SyncVar` -```cs -[SyncVar(hook = nameof(HookName))] -``` +{{{ Path:'Snippets/Sync/SyncVarHookExamples.cs' Name:'SyncVarHookAttribute' }}} A hook can be a method or a event, when using an event it should use `System.Action`. @@ -18,19 +16,7 @@ The hook can have 0, 1 or 2 args. -```cs -void hook0() { } - -void hook1(int newValue) { } - -void hook2(int oldValue, int newValue) { } - -event Action event0; - -event Action event1; - -event Action event2; -``` +{{{ Path:'Snippets/Sync/SyncVarHookExamples.cs' Name:'HookSignatures' }}} ## When is hook invoked? diff --git a/doc/docs/guides/sync/sync-var.md b/doc/docs/guides/sync/sync-var.md index 1bd38a37a9b..3b0563dbf65 100644 --- a/doc/docs/guides/sync/sync-var.md +++ b/doc/docs/guides/sync/sync-var.md @@ -30,50 +30,14 @@ Because the Weaver completely clears the compiled method body of the getter and ## Example Let's have a simple `Player` class with the following code: -``` cs -using Mirage; -using UnityEngine; - -public class Player : NetworkBehaviour -{ - [SyncVar] - public int clickCount { get; set; } - - private void Update() - { - if (IsLocalPlayer && Input.GetMouseButtonDown(0)) - { - ServerRpc_IncreaseClicks(); - } - } - - [ServerRpc] - public void ServerRpc_IncreaseClicks() - { - // This is executed on the server - clickCount++; - } -} -``` +{{{ Path:'Snippets/Sync/SyncVarExamples.cs' Name:'SyncVarBasicExample' }}} In this example, when Player A clicks the left mouse button, he sends a [ServerRpc](/docs/guides/remote-actions/server-rpc) to the server where the `clickCount` SyncVar is incremented. All other visible players will be informed about Player A's new `clickCount` value. ## Class inheritance SyncVars work with class inheritance. Consider this example: -```cs -private class Pet : NetworkBehaviour -{ - [SyncVar] - private string name { get; set; } -} - -private class Cat : Pet -{ - [SyncVar] - private Color32 color { get; set; } -} -``` +{{{ Path:'Snippets/Sync/SyncVarExamples.cs' Name:'SyncVarInheritanceExample' }}} You can attach the Cat component to your cat prefab, and it will synchronize both its `name` and `color`. @@ -89,96 +53,12 @@ For more information on SyncVar hooks see [Sync Var Hooks](/docs/guides/sync/syn ### Example Client Only Below is a simple example of assigning a random color to each player when they're spawned on the server. All clients will see all players in the correct colors, even if they join later. -```cs -using UnityEngine; -using Mirage; - -public class Player : NetworkBehaviour -{ - [SyncVar(hook = nameof(UpdateColor))] - private Color playerColor { get; set; } = Color.black; - - private Renderer renderer; - - // Unity makes a clone of the Material every time renderer.material is used. - // Cache it here and Destroy it in OnDestroy to prevent a memory leak. - private Material cachedMaterial; - - private void Awake() - { - renderer = GetComponent(); - Identity.OnStartServer.AddListener(OnStartServer); - } - - private void OnStartServer() - { - playerColor = Random.ColorHSV(0f, 1f, 1f, 1f, 0.5f, 1f); - } - - private void UpdateColor(Color oldColor, Color newColor) - { - // this is executed on this player for each client - if (cachedMaterial == null) - { - cachedMaterial = renderer.material; - } - - cachedMaterial.color = newColor; - } - - private void OnDestroy() - { - Destroy(cachedMaterial); - } -} -``` +{{{ Path:'Snippets/Sync/SyncVarExamples.cs' Name:'SyncVarClientOnlyHookExample' }}} ### Example Client & Server Below is a simple example of assigning a random color to each player when they're spawned on the server. All clients will see all players in the correct colors, even if they join later, the server will also fire the event. -```cs -using UnityEngine; -using Mirage; - -public class Player : NetworkBehaviour -{ - [SyncVar(hook = nameof(UpdateColor), invokeHookOnServer = true)] - private Color playerColor { get; set; } = Color.black; - - private Renderer renderer; - - // Unity makes a clone of the Material every time renderer.material is used. - // Cache it here and Destroy it in OnDestroy to prevent a memory leak. - private Material cachedMaterial; - - private void Awake() - { - renderer = GetComponent(); - Identity.OnStartServer.AddListener(OnStartServer); - } - - private void OnStartServer() - { - playerColor = Random.ColorHSV(0f, 1f, 1f, 1f, 0.5f, 1f); - } - - private void UpdateColor(Color oldColor, Color newColor) - { - // this is executed on this player for each client - if (cachedMaterial == null) - { - cachedMaterial = renderer.material; - } - - cachedMaterial.color = newColor; - } - - private void OnDestroy() - { - Destroy(cachedMaterial); - } -} -``` +{{{ Path:'Snippets/Sync/SyncVarExamples.cs' Name:'SyncVarServerClientHookExample' }}} ## SyncVar Initialize Only @@ -192,65 +72,7 @@ Syncvar Hooks become redundant, as you are setting the state of the Syncvar dire ### Example -``` cs -using Mirage; -using UnityEngine; - -public class Player : NetworkBehaviour -{ - [SyncVar(initialOnly = true)] - private int weaponId { get; set; } - - private void Awake() - { - Identity.OnStartClient.AddListener(OnStartClient); - } - - private void OnStartClient() - { - // Update weapon using id from syncvar (sent to client via spawn message - UpdateWeapon(weaponId); - } - - private void Update() - { - if (Input.GetKeyDown(KeyCode.Q)) - { - // Client Request weapon change - ServerRpc_SetSyncVarWeaponId(7); - } - } - - [ServerRpc] - private void ServerRpc_SetSyncVarWeaponId(int weaponId) - { - // Set weapon id on server so new players get it - this.weaponId = weaponId; - - // Tell current players about it - ClientRpc_SetSyncVarWeaponId(weaponId); - - // Update weapon on server - UpdateWeapon(weaponId); - } - - [ClientRpc] - private void ClientRpc_SetSyncVarWeaponId(int weaponId) - { - // Set id on client - this.weaponId = weaponId; - - // Update weapon on client - UpdateWeapon(weaponId); - } - - public void UpdateWeapon(int weaponId) - { - // Do stuff to update weapon here - // For example, its spawning model - } -} -``` +{{{ Path:'Snippets/Sync/SyncVarExamples.cs' Name:'SyncVarInitialOnlyExample' }}} ## Protecting SyncVars from Allocation Attacks @@ -266,9 +88,8 @@ public class Player : NetworkBehaviour { // Restricts the display name to a maximum of 32 characters [SyncVar, MaxLength(32)] - public string displayName; + public string displayName { get; set; } } ``` Applying `[MaxLength(N)]` ensures that if a serialized update exceeds the maximum character count for strings or the maximum element count for collections, Mirage throws a `SerializationLimitException` before the allocation occurs. This aborts deserialization, flags the sender with `PlayerErrorFlags.SerializationLimit`, and applies an error rate-limit penalty of 100 on the connection. - From 08cab5d2b542405f88ee597e7a4c2b7e9457b9d3 Mon Sep 17 00:00:00 2001 From: James Frowen Date: Mon, 1 Jun 2026 22:24:45 +0100 Subject: [PATCH 25/41] fixing rule MIRAGE1401 --- .../AwakeStartNetworkStateTests.cs | 39 ++++++++++------ .../Negative_AccessUnsafePropertiesInAwake.cs | 44 ++++++++++++++++++ .../Positive_AccessInAllowedMethods.cs | 14 +++++- ... => Positive_AccessSyncVarFieldInAwake.cs} | 2 +- ... Positive_AccessSyncVarPropertyInStart.cs} | 2 +- .../MirageAnalyzer.Lifecycles.cs | 46 ++++++++++--------- doc/docs/analyzers/MIRAGE1401.md | 10 +++- doc/docs/analyzers/index.md | 2 +- 8 files changed, 119 insertions(+), 40 deletions(-) create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Negative_AccessUnsafePropertiesInAwake.cs rename CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/{Negative_AccessSyncVarFieldInAwake.cs => Positive_AccessSyncVarFieldInAwake.cs} (82%) rename CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/{Negative_AccessSyncVarPropertyInStart.cs => Positive_AccessSyncVarPropertyInStart.cs} (83%) diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/AwakeStartNetworkStateTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/AwakeStartNetworkStateTests.cs index cd44861d5e1..db1c58e804d 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/AwakeStartNetworkStateTests.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/AwakeStartNetworkStateTests.cs @@ -32,25 +32,38 @@ public async Task Negative_AccessIsServerInAwake() } [Test] - public async Task Negative_AccessSyncVarPropertyInStart() + public async Task Negative_AccessUnsafePropertiesInAwake() { - var code = VerifyCS.LoadTestData("AwakeStart/Negative_AccessSyncVarPropertyInStart.cs"); - var expected = VerifyCS.Diagnostic("MIRAGE1401") - .WithLocation(0) - .WithArguments("Health", "Start"); + var code = VerifyCS.LoadTestData("AwakeStart/Negative_AccessUnsafePropertiesInAwake.cs"); + var expected0 = VerifyCS.Diagnostic("MIRAGE1401").WithLocation(0).WithArguments("Server", "Awake"); + var expected1 = VerifyCS.Diagnostic("MIRAGE1401").WithLocation(1).WithArguments("Client", "Awake"); + var expected2 = VerifyCS.Diagnostic("MIRAGE1401").WithLocation(2).WithArguments("World", "Awake"); + var expected3 = VerifyCS.Diagnostic("MIRAGE1401").WithLocation(3).WithArguments("ServerObjectManager", "Awake"); + var expected4 = VerifyCS.Diagnostic("MIRAGE1401").WithLocation(4).WithArguments("ClientObjectManager", "Awake"); + var expected5 = VerifyCS.Diagnostic("MIRAGE1401").WithLocation(5).WithArguments("Visibility", "Awake"); + var expected6 = VerifyCS.Diagnostic("MIRAGE1401").WithLocation(6).WithArguments("SyncVarSender", "Awake"); + var expected7 = VerifyCS.Diagnostic("MIRAGE1401").WithLocation(7).WithArguments("MyServerRpc", "Awake"); + var expected8 = VerifyCS.Diagnostic("MIRAGE1401").WithLocation(8).WithArguments("MyClientRpc", "Awake"); + var expected9 = VerifyCS.Diagnostic("MIRAGE1401").WithLocation(9).WithArguments("MyServerMethod", "Awake"); + var expected10 = VerifyCS.Diagnostic("MIRAGE1401").WithLocation(10).WithArguments("MyClientMethod", "Awake"); + var expected11 = VerifyCS.Diagnostic("MIRAGE1401").WithLocation(11).WithArguments("MyHasAuthorityMethod", "Awake"); + var expected12 = VerifyCS.Diagnostic("MIRAGE1401").WithLocation(12).WithArguments("MyLocalPlayerMethod", "Awake"); + var expected13 = VerifyCS.Diagnostic("MIRAGE1401").WithLocation(13).WithArguments("MyNetworkFlagsMethod", "Awake"); - await VerifyCS.VerifyAnalyzerAsync(code, expected); + await VerifyCS.VerifyAnalyzerAsync(code, expected0, expected1, expected2, expected3, expected4, expected5, expected6, expected7, expected8, expected9, expected10, expected11, expected12, expected13); } - [Test] - public async Task Negative_AccessSyncVarFieldInAwake() + public async Task Positive_AccessSyncVarPropertyInStart() { - var code = VerifyCS.LoadTestData("AwakeStart/Negative_AccessSyncVarFieldInAwake.cs"); - var expected = VerifyCS.Diagnostic("MIRAGE1401") - .WithLocation(0) - .WithArguments("points", "Awake"); + var code = VerifyCS.LoadTestData("AwakeStart/Positive_AccessSyncVarPropertyInStart.cs"); + await VerifyCS.VerifyAnalyzerAsync(code); + } - await VerifyCS.VerifyAnalyzerAsync(code, expected); + [Test] + public async Task Positive_AccessSyncVarFieldInAwake() + { + var code = VerifyCS.LoadTestData("AwakeStart/Positive_AccessSyncVarFieldInAwake.cs"); + await VerifyCS.VerifyAnalyzerAsync(code); } [Test] diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Negative_AccessUnsafePropertiesInAwake.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Negative_AccessUnsafePropertiesInAwake.cs new file mode 100644 index 00000000000..92f56ce2c49 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Negative_AccessUnsafePropertiesInAwake.cs @@ -0,0 +1,44 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc] + private void MyServerRpc() {} + + [ClientRpc] + private void MyClientRpc() {} + + [Server] + private void MyServerMethod() {} + + [Client] + private void MyClientMethod() {} + + [HasAuthority] + private void MyHasAuthorityMethod() {} + + [LocalPlayer] + private void MyLocalPlayerMethod() {} + + [NetworkMethod(NetworkFlags.Active)] + private void MyNetworkFlagsMethod() {} + + private void Awake() + { + var server = {|#0:Server|}; + var client = {|#1:Client|}; + var world = {|#2:World|}; + var som = {|#3:ServerObjectManager|}; + var com = {|#4:ClientObjectManager|}; + var visibility = {|#5:Visibility|}; + var svs = Identity.{|#6:SyncVarSender|}; + + {|#7:MyServerRpc|}(); + {|#8:MyClientRpc|}(); + {|#9:MyServerMethod|}(); + {|#10:MyClientMethod|}(); + {|#11:MyHasAuthorityMethod|}(); + {|#12:MyLocalPlayerMethod|}(); + {|#13:MyNetworkFlagsMethod|}(); + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Positive_AccessInAllowedMethods.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Positive_AccessInAllowedMethods.cs index fb7126d4a46..fca908f2f55 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Positive_AccessInAllowedMethods.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Positive_AccessInAllowedMethods.cs @@ -6,7 +6,19 @@ public class ValidBehaviour : NetworkBehaviour public int Health { get; set; } [SyncVar] - public int Points { get; set; } + public int Points; + + public void Awake() + { + Health = 100; + Points = 10; + } + + public void Start() + { + var h = Health; + var p = Points; + } public void Update() { diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Negative_AccessSyncVarFieldInAwake.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Positive_AccessSyncVarFieldInAwake.cs similarity index 82% rename from CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Negative_AccessSyncVarFieldInAwake.cs rename to CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Positive_AccessSyncVarFieldInAwake.cs index 07a08c43cbb..42091b3f8fa 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Negative_AccessSyncVarFieldInAwake.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Positive_AccessSyncVarFieldInAwake.cs @@ -7,6 +7,6 @@ public class MyBehaviour : NetworkBehaviour private void Awake() { - var p = {|#0:points|}; + var p = points; } } diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Negative_AccessSyncVarPropertyInStart.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Positive_AccessSyncVarPropertyInStart.cs similarity index 83% rename from CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Negative_AccessSyncVarPropertyInStart.cs rename to CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Positive_AccessSyncVarPropertyInStart.cs index e856a6010ed..24bc7f95244 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Negative_AccessSyncVarPropertyInStart.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/AwakeStart/Positive_AccessSyncVarPropertyInStart.cs @@ -7,6 +7,6 @@ public class MyBehaviour : NetworkBehaviour private void Start() { - {|#0:Health|} = 100; + Health = 100; } } diff --git a/CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.Lifecycles.cs b/CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.Lifecycles.cs index 74cfbd325bc..ad0a054b885 100644 --- a/CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.Lifecycles.cs +++ b/CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.Lifecycles.cs @@ -92,38 +92,42 @@ private void CheckNode(ExpressionSyntax node) if (symbol == null) return; - if (symbol is IPropertySymbol propertySymbol) + if (symbol is IMethodSymbol methodSymbol) { - var name = propertySymbol.Name; - if (name == "IsServer" || name == "IsClient" || name == "HasAuthority" || - name == "IsLocalPlayer" || name == "IsOwner" || name == "IsHost") - { - if (MirageTypes.NetworkBehaviour.IsOrInherits(propertySymbol.ContainingType)) - { - var diagnostic = Diagnostic.Create(MirageRules.LifecycleNetworkStateRule, node.GetLocation(), name, _context.ContainingSymbol?.Name ?? "Unknown"); - _context.ReportDiagnostic(diagnostic); - return; - } - } - - if (MirageAttributes.SyncVar.Has(propertySymbol)) + if (MirageAttributes.ServerRpc.Has(methodSymbol) || + MirageAttributes.ClientRpc.Has(methodSymbol) || + MirageAttributes.Server.Has(methodSymbol) || + MirageAttributes.Client.Has(methodSymbol) || + MirageAttributes.HasAuthority.Has(methodSymbol) || + MirageAttributes.LocalPlayer.Has(methodSymbol) || + MirageAttributes.NetworkMethod.Has(methodSymbol)) { - var diagnostic = Diagnostic.Create(MirageRules.LifecycleNetworkStateRule, node.GetLocation(), propertySymbol.Name, _context.ContainingSymbol?.Name ?? "Unknown"); + var diagnostic = Diagnostic.Create(MirageRules.LifecycleNetworkStateRule, node.GetLocation(), methodSymbol.Name, _context.ContainingSymbol?.Name ?? "Unknown"); _context.ReportDiagnostic(diagnostic); return; } } - else if (symbol is IFieldSymbol fieldSymbol) + else if (symbol is IPropertySymbol || symbol is IFieldSymbol) { - if (MirageAttributes.SyncVar.Has(fieldSymbol)) + var name = symbol.Name; + if (name == "IsServer" || name == "IsClient" || name == "HasAuthority" || + name == "IsLocalPlayer" || name == "IsOwner" || name == "IsHost" || + name == "Server" || name == "World" || name == "SyncVarSender" || + name == "ServerObjectManager" || name == "Client" || name == "ClientObjectManager" || + name == "Visibility") { - var diagnostic = Diagnostic.Create(MirageRules.LifecycleNetworkStateRule, node.GetLocation(), fieldSymbol.Name, _context.ContainingSymbol?.Name ?? "Unknown"); - _context.ReportDiagnostic(diagnostic); - return; + var containingType = symbol.ContainingType; + if (containingType != null && + (MirageTypes.NetworkBehaviour.IsOrInherits(containingType) || + MirageTypes.NetworkIdentity.IsOrInherits(containingType))) + { + var diagnostic = Diagnostic.Create(MirageRules.LifecycleNetworkStateRule, node.GetLocation(), name, _context.ContainingSymbol?.Name ?? "Unknown"); + _context.ReportDiagnostic(diagnostic); + return; + } } } } - } private class BaseCallWalker : CSharpSyntaxWalker { diff --git a/doc/docs/analyzers/MIRAGE1401.md b/doc/docs/analyzers/MIRAGE1401.md index 08af7969a7a..6de3c256c60 100644 --- a/doc/docs/analyzers/MIRAGE1401.md +++ b/doc/docs/analyzers/MIRAGE1401.md @@ -1,9 +1,15 @@ # MIRAGE1401: Accessing Network State in Awake/Start ## The Problem -Reading or writing network states (such as `IsServer`, `IsClient`, `HasAuthority`, or `SyncVar` values) inside Unity's lifecycle methods `Awake` or `Start`. -Unity's `Awake` and `Start` methods are called during GameObject initialization. At this point, Mirage's network identity is not yet spawned or initialized, meaning properties like `IsServer`, `IsClient`, and authority states are not set, and `SyncVars` have not been initialized with their network values. Accessing them inside `Awake` or `Start` results in incorrect behavior, default values, or race conditions. +Reading, writing, or invoking any of the following network properties, fields, or methods inside Unity's lifecycle methods `Awake` or `Start`: + +* **Helper Properties**: `IsServer`, `IsClient`, `IsHost`, `IsLocalPlayer`, `IsOwner`, `HasAuthority` +* **Network References**: `Server`, `Client`, `World`, `SyncVarSender`, `ServerObjectManager`, `ClientObjectManager`, `Visibility` +* **Remote Procedure Calls**: Any method decorated with `[ServerRpc]` or `[ClientRpc]` +* **Network Attributes**: Methods decorated with `[Server]`, `[Client]`, `[HasAuthority]`, `[LocalPlayer]`, or `[NetworkMethod]` + +Unity's `Awake` and `Start` methods are called during GameObject initialization before Mirage's network identity is spawned or initialized. At this point, properties and fields representing the network state or references are not set (they are null or default). Accessing them, invoking RPC methods, or calling attribute-guarded methods inside `Awake` or `Start` leads to incorrect behavior, `NullReferenceException` at runtime, default values, or race conditions. --- diff --git a/doc/docs/analyzers/index.md b/doc/docs/analyzers/index.md index 30461eefbf0..4a5b1d5db3f 100644 --- a/doc/docs/analyzers/index.md +++ b/doc/docs/analyzers/index.md @@ -22,7 +22,7 @@ Mirage uses Roslyn Analyzers to provide compile-time validation for network code | [MIRAGE1301](MIRAGE1301.md) | Message or RPC Class Warning | Warning | Warns about class types used inside network messages or RPC parameters because they cause GC allocations. | | [MIRAGE1302](MIRAGE1302.md) | Field Type Serialization Validation | Error | Confirms all fields in network messages/RPCs are serializable by Mirage or have registered custom serializers. | | [MIRAGE1303](MIRAGE1303.md) | Mismatched Custom Serialization Methods | Error | Requires custom serializers to contain matching, properly-signed reader and writer extension methods. | -| [MIRAGE1401](MIRAGE1401.md) | Accessing Network State in Awake/Start | Warning | Warns against accessing network states like `IsServer` or `SyncVars` during early Unity lifecycle phases. | +| [MIRAGE1401](MIRAGE1401.md) | Accessing Network State in Awake/Start | Warning | Warns against accessing network states like `IsServer` during early Unity lifecycle phases. | | [MIRAGE1402](MIRAGE1402.md) | Missing base Call in OnSerialize/OnDeserialize | Warning | Ensures overriding `OnSerialize` or `OnDeserialize` in derived classes calls the base implementation. | | [MIRAGE1501](MIRAGE1501.md) | Network Message Exceeds Safe MTU | Warning | Warns when a message exceeds the safe Maximum Transmission Unit (MTU) to prevent IP fragmentation. | | [MIRAGE1502](MIRAGE1502.md) | Unbounded String or Collection | Warning | Warns about unbounded strings/collections in network messages that could trigger memory exploitation. | From e43b48d6bd1e2b825ec4684068638bee29d39155 Mon Sep 17 00:00:00 2001 From: James Frowen Date: Mon, 1 Jun 2026 22:44:17 +0100 Subject: [PATCH 26/41] fix snippet compile errors --- .../Samples~/Snippets/Analyzers/Mirage1503.cs | 3 +- .../Components/NetworkDiscoverySnippet.cs | 3 +- .../Snippets/GameObjects/MyNetworkManager.cs | 67 +++++++++++-------- .../Snippets/General/ErrorHandlingSnippets.cs | 40 ++++------- .../Samples~/Snippets/Guides/FaqSnippets.cs | 7 +- .../SceneLoading/CustomSceneManager.cs | 7 +- doc/docs/guides/error-handling.md | 30 ++++++++- 7 files changed, 85 insertions(+), 72 deletions(-) diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1503.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1503.cs index 10b44762e02..f17b8589362 100644 --- a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1503.cs +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1503.cs @@ -1,4 +1,3 @@ -using Mirage; using Mirage.Serialization; namespace Mirage.Snippets.Analyzers @@ -29,7 +28,7 @@ public class Player : NetworkBehaviour public int Health { get; set; } // Correct: Compress float with a defined range and precision - [SyncVar, FloatPack(-10f, 10f, 0.01f)] + [SyncVar, FloatPack(10f, 0.01f)] public float PlayerScale { get; set; } } // CodeEmbed-End: mirage1503-resolved diff --git a/Assets/Mirage/Samples~/Snippets/Components/NetworkDiscoverySnippet.cs b/Assets/Mirage/Samples~/Snippets/Components/NetworkDiscoverySnippet.cs index 4290903de5c..43a8970c915 100644 --- a/Assets/Mirage/Samples~/Snippets/Components/NetworkDiscoverySnippet.cs +++ b/Assets/Mirage/Samples~/Snippets/Components/NetworkDiscoverySnippet.cs @@ -21,9 +21,10 @@ public class DiscoveryRequest // in their broadcast messages that servers will consume. } + public enum GameMode { PvP, PvE } + public class DiscoveryResponse { - public enum GameMode { PvP, PvE } // you probably want uri so clients know how to connect to the server public Uri uri; diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/MyNetworkManager.cs b/Assets/Mirage/Samples~/Snippets/GameObjects/MyNetworkManager.cs index ad9ee75fe08..987248bb89e 100644 --- a/Assets/Mirage/Samples~/Snippets/GameObjects/MyNetworkManager.cs +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/MyNetworkManager.cs @@ -1,69 +1,78 @@ using UnityEngine; -using Mirage; namespace Mirage.Snippets.GameObjects { // CodeEmbed-Start: spawning-without-network-manager-1 - public class MyNetworkManager : MonoBehaviour + public class MyNetworkManager : MonoBehaviour { - public GameObject treePrefab; + // Assign values in inspector + public NetworkIdentity treePrefab; public ClientObjectManager ClientObjectManager; - public NetworkClient NetworkClient; - public NetworkServer NetworkServer; + public NetworkClient Client; + public NetworkServer Server; public ServerObjectManager ServerObjectManager; - void Start() + private void Awake() { - ClientObjectManager = FindObjectOfType(); - NetworkClient = FindObjectOfType(); - NetworkServer = FindObjectOfType(); - ServerObjectManager = FindObjectOfType(); + // it is best to add events once in awake + // this avoids the need to remove or clean them up, + // which is ok for Manager classes that live as long as the NetworkServer/Client themselves + + Server.Started.AddListener(OnServerStarted); + // use Authenticated instead of Connected to ensure that player is fully setup + Server.Authenticated.AddListener(OnServerConnect); + Client.Authenticated.AddListener(OnClientConnect); } - // Register prefab and connect to the server - public void ClientConnect() + // Register prefab and connect to the server + // Call this from your UI or other code + public void StartClient(string address) { ClientObjectManager.spawnPrefabs.Add(treePrefab); - NetworkClient.Connect("localhost"); - NetworkClient.MessageHandler.RegisterHandler(OnClientConnect); + + Client.Connect(address); } - void OnClientConnect(NetworkConnection conn, ConnectMessage msg) + private void OnClientConnect(INetworkPlayer player) { - Debug.Log("Connected to server: " + conn); + Debug.Log("Connected to server: " + player); } // CodeEmbed-End: spawning-without-network-manager-1 // CodeEmbed-Start: spawning-without-network-manager-2 - public void ServerListen() + public void StartServer() { - // start listening, and allow up to 4 connections - NetworkServer.StartServer(); + // start listening + Server.StartServer(); + } - NetworkServer.MessageHandler.RegisterHandler(OnServerConnect); - NetworkServer.MessageHandler.RegisterHandler(OnClientReady); + private void OnServerStarted() + { + // it is best to register message from .Started event + // this means they will be added early enough for host player to use them + Server.MessageHandler.RegisterHandler(HandleSceneReadyMessage); } // When client is ready spawn a few trees - void OnClientReady(NetworkConnection conn, ReadyMessage msg) + private void HandleSceneReadyMessage(INetworkPlayer player, SceneReadyMessage msg) { - Debug.Log("Client is ready to start: " + conn); + Debug.Log("Client is ready to start: " + player); SpawnTrees(); } - void SpawnTrees() + private void SpawnTrees() { - int x = 0; - for (int i = 0; i < 5; ++i) + var x = 0; + for (var i = 0; i < 5; ++i) { - GameObject treeGo = Instantiate(treePrefab, new Vector3(x++, 0, 0), Quaternion.identity); + var treeGo = Instantiate(treePrefab, new Vector3(x++, 0, 0), Quaternion.identity); ServerObjectManager.Spawn(treeGo); } } - void OnServerConnect(NetworkConnection conn, ConnectMessage msg) + private void OnServerConnect(INetworkPlayer player) { - Debug.Log("New client connected: " + conn); + Debug.Log("New client connected: " + player); } // CodeEmbed-End: spawning-without-network-manager-2 } diff --git a/Assets/Mirage/Samples~/Snippets/General/ErrorHandlingSnippets.cs b/Assets/Mirage/Samples~/Snippets/General/ErrorHandlingSnippets.cs index 89c2f99d93b..7472213e4b5 100644 --- a/Assets/Mirage/Samples~/Snippets/General/ErrorHandlingSnippets.cs +++ b/Assets/Mirage/Samples~/Snippets/General/ErrorHandlingSnippets.cs @@ -1,33 +1,7 @@ -using System; using UnityEngine; namespace Mirage.Snippets.General { - // CodeEmbed-Start: error-handling-flags - // see PlayerErrorFlags in the source code for most up-to-date values - [Flags] - public enum PlayerErrorFlags - { - None = 0, - - // Likely developer bugs - RpcNullException = 1 << 0, - RpcException = 1 << 1, - - // Connection/versioning issues - DeserializationException = 1 << 2, - RpcSync = 1 << 3, - RateLimit = 1 << 4, - - // Security/Malicious Intent - Unauthorized = 1 << 5, - Critical = 1 << 6, - LikelyCheater = 1 << 7, - - // Custom developer defined errors - CustomError = 1 << 16 - } - // CodeEmbed-End: error-handling-flags // CodeEmbed-Start: error-handling-custom-flags public static class MyErrorFlags @@ -48,7 +22,12 @@ namespace Mirage.Snippets.General.CustomError // CodeEmbed-Start: error-handling-custom-error-class public static class MyErrorFlags { - public const PlayerErrorFlags InvalidAction = PlayerErrorFlags.CustomError; + public static readonly PlayerErrorFlags InvalidAction = Custom(0); + + private static PlayerErrorFlags Custom(int index) + { + return (PlayerErrorFlags)(((int)PlayerErrorFlags.CustomError) << index); + } } // CodeEmbed-End: error-handling-custom-error-class @@ -145,7 +124,12 @@ namespace Mirage.Snippets.General.CustomErrorHandler { public static class MyErrorFlags { - public const PlayerErrorFlags InvalidAction = PlayerErrorFlags.CustomError << 0; + public static readonly PlayerErrorFlags InvalidAction = Custom(0); + + private static PlayerErrorFlags Custom(int index) + { + return (PlayerErrorFlags)(((int)PlayerErrorFlags.CustomError) << index); + } } // CodeEmbed-Start: error-handling-custom-handler diff --git a/Assets/Mirage/Samples~/Snippets/Guides/FaqSnippets.cs b/Assets/Mirage/Samples~/Snippets/Guides/FaqSnippets.cs index 1958302ffad..45815cc0e9e 100644 --- a/Assets/Mirage/Samples~/Snippets/Guides/FaqSnippets.cs +++ b/Assets/Mirage/Samples~/Snippets/Guides/FaqSnippets.cs @@ -1,4 +1,3 @@ -using Mirage; using UnityEngine; namespace Mirage.Snippets.Guides @@ -12,10 +11,10 @@ public void RpcDoSomething(MyCustomStruct data) // do stuff here } - struct MyCustomStruct + public struct MyCustomStruct { - int someNumber; - Vector3 somePosition; + public int someNumber; + public Vector3 somePosition; } // CodeEmbed-End: faq-custom-data } diff --git a/Assets/Mirage/Samples~/Snippets/SceneLoading/CustomSceneManager.cs b/Assets/Mirage/Samples~/Snippets/SceneLoading/CustomSceneManager.cs index 8004ee81a0f..e6b6d1c698f 100644 --- a/Assets/Mirage/Samples~/Snippets/SceneLoading/CustomSceneManager.cs +++ b/Assets/Mirage/Samples~/Snippets/SceneLoading/CustomSceneManager.cs @@ -1,6 +1,3 @@ -using UnityEngine; -using Mirage; - namespace Mirage.Snippets.SceneLoading.OverrideOnServerAuthenticated { #pragma warning disable CS0618 // Type or member is obsolete @@ -24,7 +21,7 @@ namespace Mirage.Snippets.SceneLoading.OverrideStart // CodeEmbed-Start: custom-scene-manager-start public class MySceneManager : NetworkSceneManager { - protected internal override void Start() + public override void Start() { // add your stuff before. @@ -43,7 +40,7 @@ namespace Mirage.Snippets.SceneLoading.OverrideOnDestroy // CodeEmbed-Start: custom-scene-manager-on-destroy public class MySceneManager : NetworkSceneManager { - protected internal override void OnDestroy() + public override void OnDestroy() { // add your stuff before. diff --git a/doc/docs/guides/error-handling.md b/doc/docs/guides/error-handling.md index cf0c053ed1a..ee8c51647b6 100644 --- a/doc/docs/guides/error-handling.md +++ b/doc/docs/guides/error-handling.md @@ -15,9 +15,33 @@ This rate-limiting system is server-side only and replaces the old `DisconnectOn ## Player Error Flags -The `PlayerErrorFlags` enum helps categorize the types of errors a player can cause, allowing for more granular tracking and response. - -{{{ Path:'Snippets/General/ErrorHandlingSnippets.cs' Name:'error-handling-flags' }}} +```cs +// see PlayerErrorFlags in the source code for most up-to-date values +[Flags] +public enum PlayerErrorFlags +{ + None = 0, + + // Likely developer bugs + RpcNullException = 1 << 0, + RpcException = 1 << 1, + + // Connection/versioning issues + DeserializationException = 1 << 2, + RpcSync = 1 << 3, + RateLimit = 1 << 4, + InvalidState = 1 << 9, + + // Security/Malicious Intent + NoAuthority = 1 << 5, + Unauthenticated = 1 << 6, + Critical = 1 << 7, + LikelyCheater = 1 << 8, + + // Custom developer defined errors + CustomError = 1 << 16 +} +``` * **`SerializationLimit`**: Triggered when a client sends a payload (like a string, list, or array) whose size exceeds the limit defined by the `[MaxLength]` attribute, throwing a `SerializationLimitException`. This carries a cost of `100` to quickly penalize and disconnect potentially malicious clients. From 28751c9250e2ab6cc14751ed244f6ca304df8026 Mon Sep 17 00:00:00 2001 From: James Frowen Date: Mon, 1 Jun 2026 23:07:13 +0100 Subject: [PATCH 27/41] fixing snippet compile errors --- .../Snippets/GameObjects/MyNetworkManager.cs | 17 ++++-- .../Samples~/Snippets/GameObjects/Tree.cs | 32 +++++----- .../Serialization/DataTypesSnippets.cs | 58 ++++++++++--------- .../Samples~/Snippets/Sync/CodeGeneration.cs | 26 ++++----- .../Snippets/Sync/SyncHashSetExamples.cs | 40 +++++++------ .../Snippets/Sync/SyncSortedSetExamples.cs | 40 +++++++------ .../Snippets/Sync/SyncVarHookExamples.cs | 2 +- doc/docs/guides/game-objects/spawn-object.md | 32 +++++----- doc/docs/guides/serialization/data-types.md | 11 +--- 9 files changed, 133 insertions(+), 125 deletions(-) diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/MyNetworkManager.cs b/Assets/Mirage/Samples~/Snippets/GameObjects/MyNetworkManager.cs index 987248bb89e..3cfcfc1e9c7 100644 --- a/Assets/Mirage/Samples~/Snippets/GameObjects/MyNetworkManager.cs +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/MyNetworkManager.cs @@ -1,8 +1,12 @@ using UnityEngine; +using Mirage; namespace Mirage.Snippets.GameObjects { // CodeEmbed-Start: spawning-without-network-manager-1 + [NetworkMessage] + public struct SpawnTreeMessage {} + public class MyNetworkManager : MonoBehaviour { // Assign values in inspector @@ -28,7 +32,8 @@ private void Awake() // Call this from your UI or other code public void StartClient(string address) { - ClientObjectManager.spawnPrefabs.Add(treePrefab); + if (!ClientObjectManager.spawnPrefabs.Contains(treePrefab)) + ClientObjectManager.spawnPrefabs.Add(treePrefab); Client.Connect(address); } @@ -36,6 +41,8 @@ public void StartClient(string address) private void OnClientConnect(INetworkPlayer player) { Debug.Log("Connected to server: " + player); + // Request the server to spawn trees + player.Send(new SpawnTreeMessage()); } // CodeEmbed-End: spawning-without-network-manager-1 @@ -50,13 +57,13 @@ private void OnServerStarted() { // it is best to register message from .Started event // this means they will be added early enough for host player to use them - Server.MessageHandler.RegisterHandler(HandleSceneReadyMessage); + Server.MessageHandler.RegisterHandler(HandleSpawnTreeMessage); } - // When client is ready spawn a few trees - private void HandleSceneReadyMessage(INetworkPlayer player, SceneReadyMessage msg) + // When client requests to spawn trees + private void HandleSpawnTreeMessage(INetworkPlayer player, SpawnTreeMessage msg) { - Debug.Log("Client is ready to start: " + player); + Debug.Log("Client requested to spawn trees: " + player); SpawnTrees(); } diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/Tree.cs b/Assets/Mirage/Samples~/Snippets/GameObjects/Tree.cs index 1299645b925..6b564aeedae 100644 --- a/Assets/Mirage/Samples~/Snippets/GameObjects/Tree.cs +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/Tree.cs @@ -21,35 +21,33 @@ public void OnStartClient() } // CodeEmbed-End: tree-syncvar-example - public class TreeAuthorityExample : NetworkBehaviour +} + +namespace Mirage.Snippets.GameObjects.ClientAuthority +{ + // CodeEmbed-Start: tree-client-authority + public class Tree : NetworkBehaviour { - public GameObject treePrefab; - public ClientObjectManager ClientObjectManager; - public NetworkClient NetworkClient; public int numLeaves; - private void OnClientConnect(NetworkConnection conn, ConnectMessage msg) {} - - // CodeEmbed-Start: tree-client-authority - public void ClientConnect() + private void Awake() { - ClientObjectManager.spawnPrefabs.Add(treePrefab); - NetworkClient.Connect("localhost"); - NetworkClient.MessageHandler.RegisterHandler(OnClientConnect); - - NetworkClient.Player.Identity.OnAuthorityChanged.AddListener(OnStartAuthority); + // Register listener in Awake to catch any early authority changes. + Identity.OnAuthorityChanged.AddListener(OnStartAuthority); } - public void OnStartAuthority(bool changed) + public void OnStartAuthority(bool hasAuthority) { - CmdMessageFromTree("Tree with " + numLeaves + " reporting in"); + // Only execute server command when we are given authority over the tree. + if (hasAuthority) + CmdMessageFromTree("Tree with " + numLeaves + " reporting in"); } [ServerRpc] - void CmdMessageFromTree(string msg) + private void CmdMessageFromTree(string msg) { Debug.Log("Client sent a tree message: " + msg); } - // CodeEmbed-End: tree-client-authority } + // CodeEmbed-End: tree-client-authority } diff --git a/Assets/Mirage/Samples~/Snippets/Serialization/DataTypesSnippets.cs b/Assets/Mirage/Samples~/Snippets/Serialization/DataTypesSnippets.cs index 4c7a1bf8780..9030c815e07 100644 --- a/Assets/Mirage/Samples~/Snippets/Serialization/DataTypesSnippets.cs +++ b/Assets/Mirage/Samples~/Snippets/Serialization/DataTypesSnippets.cs @@ -1,7 +1,8 @@ using System; using System.Collections; -using UnityEngine; using Mirage.Serialization; +using UnityEngine; +using Cysharp.Threading.Tasks; namespace Mirage.Snippets.Serialization.GameObjectLookup { @@ -10,24 +11,25 @@ public class GameObjectLookup : NetworkBehaviour // CodeEmbed-Start: game-object-lookup public GameObject target; - [SyncVar(hook = nameof(OnTargetChanged))] - public uint targetID { get; set; } - - void OnTargetChanged(uint _, uint newValue) + [ClientRpc] + public void SetTargetRpc(uint targetID) { - if (NetworkIdentity.World.Spawned.TryGetValue(targetID, out NetworkIdentity identity)) - target = identity.gameObject; - else - StartCoroutine(SetTarget()); + SetTargetAsync(targetID).Forget(); } - IEnumerator SetTarget() + private async UniTaskVoid SetTargetAsync(uint targetID) { - while (target == null) + var token = destroyCancellationToken; + + // Wait until the target object is spawned on the client + while (!token.IsCancellationRequested) { - yield return null; - if (NetworkIdentity.World.SpawnedObjects.TryGetValue(targetID, out NetworkIdentity identity)) + if (Identity.World.TryGetIdentity(targetID, out var identity)) + { target = identity.gameObject; + return; + } + await UniTask.Yield(); } } // CodeEmbed-End: game-object-lookup @@ -43,7 +45,7 @@ public static void WriteDateTime(this NetworkWriter writer, DateTime dateTime) { writer.WriteInt64(dateTime.Ticks); } - + public static DateTime ReadDateTime(this NetworkReader reader) { return new DateTime(reader.ReadInt64()); @@ -55,26 +57,26 @@ public static DateTime ReadDateTime(this NetworkReader reader) namespace Mirage.Snippets.Serialization.PolymorphicEquip { // CodeEmbed-Start: polymorphic-equip - class Item + public class Item { public string name; } - class Weapon : Item + public class Weapon : Item { public int hitPoints; } - class Armor : Item + public class Armor : Item { public int hitPoints; public int level; } - class Player : NetworkBehaviour + public class Player : NetworkBehaviour { [ServerRpc] - void ServerRpcEquip(Item item) + private void ServerRpcEquip(Item item) { // IMPORTANT: this does not work. Mirage will pass you an object of type item // even if you pass a weapon or an armor. @@ -90,7 +92,7 @@ void ServerRpcEquip(Item item) } [ServerRpc] - void ServerEquipArmor(Armor armor) + private void ServerEquipArmor(Armor armor) { // IMPORTANT: this does not work either, you will receive an armor, but // the armor will not have a valid Item.name, even if you passed an armor with name @@ -104,10 +106,10 @@ namespace Mirage.Snippets.Serialization.PolymorphicSerializer using PolymorphicEquip; // CodeEmbed-Start: polymorphic-serializer - public static class ItemSerializer + public static class ItemSerializer { - const byte WEAPON = 1; - const byte ARMOR = 2; + private const byte WEAPON = 1; + private const byte ARMOR = 2; public static void WriteItem(this NetworkWriter writer, Item item) { @@ -129,7 +131,7 @@ public static void WriteItem(this NetworkWriter writer, Item item) public static Item ReadItem(this NetworkReader reader) { byte type = reader.ReadByte(); - switch(type) + switch (type) { case WEAPON: return new Weapon @@ -156,7 +158,7 @@ namespace Mirage.Snippets.Serialization.ScriptableObjectSerializer { // CodeEmbed-Start: scriptable-object-serializer [CreateAssetMenu(fileName = "New Armor", menuName = "Armor Data")] - class Armor : ScriptableObject + public class Armor : ScriptableObject { public int Hitpoints; public int Weight; @@ -165,12 +167,12 @@ class Armor : ScriptableObject // ... } - public static class ArmorSerializer + public static class ArmorSerializer { public static void WriteArmor(this NetworkWriter writer, Armor armor) { - // No need to serialize the data, just the name of the armor. - writer.WriteString(armor.name); + // No need to serialize the data, just the name of the armor. + writer.WriteString(armor.name); } public static Armor ReadArmor(this NetworkReader reader) diff --git a/Assets/Mirage/Samples~/Snippets/Sync/CodeGeneration.cs b/Assets/Mirage/Samples~/Snippets/Sync/CodeGeneration.cs index 13f7f16425e..c5c77fc2c8c 100644 --- a/Assets/Mirage/Samples~/Snippets/Sync/CodeGeneration.cs +++ b/Assets/Mirage/Samples~/Snippets/Sync/CodeGeneration.cs @@ -49,21 +49,21 @@ public override bool SerializeSyncVars(NetworkWriter writer, bool initialState) else { // Writes which SyncVars have changed - writer.WritePackedUInt64(base.SyncVarDirtyBits); + writer.Write(base.SyncVarDirtyBits, 3); - if ((base.SyncVarDirtyBits & 1u) != 0u) + if ((base.SyncVarDirtyBits & 1uL) != 0uL) { writer.WritePackedUInt32((uint)this.int1); written = true; } - if ((base.SyncVarDirtyBits & 2u) != 0u) + if ((base.SyncVarDirtyBits & 2uL) != 0uL) { writer.WritePackedUInt32((uint)this.int2); written = true; } - if ((base.SyncVarDirtyBits & 4u) != 0u) + if ((base.SyncVarDirtyBits & 4uL) != 0uL) { writer.Write(this.MyString); written = true; @@ -87,39 +87,33 @@ public override void DeserializeSyncVars(NetworkReader reader, bool initialState this.int1 = (int)reader.ReadPackedUInt32(); // if old and new values are not equal, call hook if (!base.SyncVarEqual(oldInt1, this.int1)) - { this.OnInt1Changed(oldInt1, this.int1); - } this.int2 = (int)reader.ReadPackedUInt32(); this.MyString = reader.ReadString(); return; } - int dirtySyncVars = (int)reader.ReadPackedUInt32(); + ulong dirtySyncVars = reader.Read(3); + base.SetDeserializeMask(dirtySyncVars, 0); + // is 1st SyncVar dirty - if ((dirtySyncVars & 1) != 0) + if ((dirtySyncVars & 1uL) != 0uL) { int oldInt1 = this.int1; this.int1 = (int)reader.ReadPackedUInt32(); // if old and new values are not equal, call hook if (!base.SyncVarEqual(oldInt1, this.int1)) - { this.OnInt1Changed(oldInt1, this.int1); - } } // is 2nd SyncVar dirty - if ((dirtySyncVars & 2) != 0) - { + if ((dirtySyncVars & 2uL) != 0uL) this.int2 = (int)reader.ReadPackedUInt32(); - } // is 3rd SyncVar dirty - if ((dirtySyncVars & 4) != 0) - { + if ((dirtySyncVars & 4uL) != 0uL) this.MyString = reader.ReadString(); - } } // CodeEmbed-End: DeserializeSyncVarsExample } diff --git a/Assets/Mirage/Samples~/Snippets/Sync/SyncHashSetExamples.cs b/Assets/Mirage/Samples~/Snippets/Sync/SyncHashSetExamples.cs index f835801be7b..ad8612b1e85 100644 --- a/Assets/Mirage/Samples~/Snippets/Sync/SyncHashSetExamples.cs +++ b/Assets/Mirage/Samples~/Snippets/Sync/SyncHashSetExamples.cs @@ -15,8 +15,8 @@ public class Player : NetworkBehaviour int skillPoints = 10; - [Command] - public void CmdLearnSkill(string skillName) + [ServerRpc] + public void LearnSkill(string skillName) { if (skillPoints > 1) { @@ -42,25 +42,31 @@ public class Player : NetworkBehaviour // this will add the delegate on the client. // Use OnStartServer instead if you want it on the server - public override void OnStartClient() + private void Awake() { - buffs.Callback += OnBuffsChanged; + Identity.OnStartClient.AddListener(OnStartClient); } - private void OnBuffsChanged(SyncSetBuffs.Operation op, string buff) + private void OnStartClient() { - switch (op) - { - case SyncSetBuffs.Operation.OP_ADD: - // we added a buff, draw an icon on the character - break; - case SyncSetBuffs.Operation.OP_CLEAR: - // clear all buffs from the character - break; - case SyncSetBuffs.Operation.OP_REMOVE: - // We removed a buff from the character - break; - } + buffs.OnAdd += OnBuffAdded; + buffs.OnRemove += OnBuffRemoved; + buffs.OnClear += OnBuffsCleared; + } + + private void OnBuffAdded(string buff) + { + // we added a buff, draw an icon on the character + } + + private void OnBuffRemoved(string buff) + { + // We removed a buff from the character + } + + private void OnBuffsCleared() + { + // clear all buffs from the character } } // CodeEmbed-End: SyncHashSetCallbackExample diff --git a/Assets/Mirage/Samples~/Snippets/Sync/SyncSortedSetExamples.cs b/Assets/Mirage/Samples~/Snippets/Sync/SyncSortedSetExamples.cs index c2cf0041351..a4fda8668cc 100644 --- a/Assets/Mirage/Samples~/Snippets/Sync/SyncSortedSetExamples.cs +++ b/Assets/Mirage/Samples~/Snippets/Sync/SyncSortedSetExamples.cs @@ -13,8 +13,8 @@ class SyncSkillSet : SyncSortedSet {} int skillPoints = 10; - [Command] - public void CmdLearnSkill(string skillName) + [ServerRpc] + public void LearnSkill(string skillName) { if (skillPoints > 1) { @@ -38,25 +38,31 @@ private class SyncSetBuffs : SyncSortedSet {} // This will add the delegate on the client. // Use OnStartServer instead if you want it on the server - public override void OnStartClient() + private void Awake() { - buffs.Callback += OnBuffsChanged; + Identity.OnStartClient.AddListener(OnStartClient); } - private void OnBuffsChanged(SyncSetBuffs.Operation op, string buff) + private void OnStartClient() { - switch (op) - { - case SyncSetBuffs.Operation.OP_ADD: - // we added a buff, draw an icon on the character - break; - case SyncSetBuffs.Operation.OP_CLEAR: - // clear all buffs from the character - break; - case SyncSetBuffs.Operation.OP_REMOVE: - // We removed a buff from the character - break; - } + buffs.OnAdd += OnBuffAdded; + buffs.OnRemove += OnBuffRemoved; + buffs.OnClear += OnBuffsCleared; + } + + private void OnBuffAdded(string buff) + { + // we added a buff, draw an icon on the character + } + + private void OnBuffRemoved(string buff) + { + // We removed a buff from the character + } + + private void OnBuffsCleared() + { + // clear all buffs from the character } } // CodeEmbed-End: SyncSortedSetCallbackExample diff --git a/Assets/Mirage/Samples~/Snippets/Sync/SyncVarHookExamples.cs b/Assets/Mirage/Samples~/Snippets/Sync/SyncVarHookExamples.cs index e6d210ad5c0..84a7ace92df 100644 --- a/Assets/Mirage/Samples~/Snippets/Sync/SyncVarHookExamples.cs +++ b/Assets/Mirage/Samples~/Snippets/Sync/SyncVarHookExamples.cs @@ -8,7 +8,7 @@ public class SyncVarHookAttributeExample : NetworkBehaviour // CodeEmbed-Start: SyncVarHookAttribute [SyncVar(hook = nameof(HookName))] // CodeEmbed-End: SyncVarHookAttribute - public int myValue; + public int myValue { get; set; } void HookName(int oldValue, int newValue) {} } diff --git a/doc/docs/guides/game-objects/spawn-object.md b/doc/docs/guides/game-objects/spawn-object.md index 1e270b35a91..7fa07e96447 100644 --- a/doc/docs/guides/game-objects/spawn-object.md +++ b/doc/docs/guides/game-objects/spawn-object.md @@ -28,7 +28,7 @@ This searches the entire project for prefabs/objects that have a network identit For more advanced users, you may find that you want to register Prefabs and spawn game objects without using the Network Manager component. -To spawn game objects without using the Network Manager, you can handle the Prefab registration yourself via script. Use the `ClientScene.RegisterPrefab` method to register Prefabs to the Network Manager. +To spawn game objects without using the Network Manager, you can handle the Prefab registration yourself via script. Add the Prefab to the `ClientObjectManager.spawnPrefabs` list, or register it using the `ClientObjectManager.RegisterPrefab` method. {{{ Path:'Snippets/GameObjects/MyNetworkManager.cs' Name:'spawning-without-network-manager-1' }}} @@ -77,23 +77,23 @@ The actual flow of internal operations that takes place for spawning game object - A network message of the type `ObjectDestroy` is sent to clients. - `OnNetworkDestroy` is called on the instance on clients, then the instance is destroyed. -### Player Game Objects +### Character Game Objects -Player game objects in the HLAPI work slightly differently from non-player game objects. The flow for spawning player game objects with the Network Manager is: -- Prefab with `NetworkIdentity` is registered as the `PlayerPrefab` -- The client connects to the server -- Client calls `AddPlayer`, network message of type `MsgType.AddPlayer` is sent to the server -- The server receives the message and calls `CharacterSpawner.OnServerAddPlayer` -- A game object is instantiated from the Player Prefab on the server -- `ServerObjectManager.AddCharacter` is called with the new player instance on the server -- The player instance is spawned - you do not have to call `ServerObjectManager.Spawn` for the player instance. The spawn message is sent to all clients like on a normal spawn. -- A network message of type `Owner` is sent to the client that added the player (only that client!) -- The original client receives the network message -- `OnStartLocalPlayer` is called on the player instance on the original client, and `IsLocalPlayer` is set to true +Character game objects in Mirage represent the network identity associated with an active player connection. The flow for spawning character game objects is: +- Prefab with `NetworkIdentity` is registered as the `PlayerPrefab` on `CharacterSpawner`. +- The client connects and authenticates with the server. +- Client sends an `AddCharacterMessage` to the server. +- The server receives the message and calls `CharacterSpawner.OnServerAddPlayer` (or your custom spawn handler). +- A game object is instantiated from the Player Prefab on the server. +- `ServerObjectManager.AddCharacter` is called with the player and the new character instance on the server. +- The character instance is spawned - you do not have to call `ServerObjectManager.Spawn` for the character instance. The spawn message is sent to all clients like on a normal spawn. +- A message is sent to the client assigning ownership of the character. +- The client receives the spawn/ownership message. +- `OnStartLocalPlayer` is called on the character instance on the owning client, and `IsLocalPlayer` is set to `true`. :::note -`OnStartLocalPlayer` is called after `OnStartClient`, because it only happens when the ownership message arrives from the server after the player game object is spawned, so `IsLocalPlayer` is not set in `OnStartClient`. -Because `OnStartLocalPlayer` is only called for the client’s local player game object, it is a good place to perform initialization that should only be done for the local player. This could include enabling input processing and enabling camera tracking for the player game object. +`OnStartLocalPlayer` is called after `OnStartClient`, because it only happens when the ownership message arrives from the server after the character game object is spawned, so `IsLocalPlayer` is not set in `OnStartClient`. +Because `OnStartLocalPlayer` is only called for the client’s local character game object, it is a good place to perform initialization that should only be done for the local character. This could include enabling input processing and enabling camera tracking for the character game object. ::: ## Spawning Game Objects with Client Authority @@ -102,7 +102,7 @@ To spawn game objects and assign authority of those game objects to a particular For these game objects, the property `HasAuthority` is true on the client with authority, and `OnStartAuthority` is called on the client with authority. That client can issue Server RPCs for that game object. On other clients (and on the host), `HasAuthority` is false. -For example, the tree spawn example above can be modified to allow the tree to have client authority like this (note that we now need to pass in a Network Player game object for the owning client’s connection): +For example, the tree spawn example above can be modified to allow the tree to have client authority like this (note that we now need to pass in the `INetworkPlayer` representing the owning client’s connection): {{{ Path:'Snippets/GameObjects/SpawningExample.cs' Name:'spawn-trees-authority-example' }}} diff --git a/doc/docs/guides/serialization/data-types.md b/doc/docs/guides/serialization/data-types.md index 676bff81ced..0ce75852034 100644 --- a/doc/docs/guides/serialization/data-types.md +++ b/doc/docs/guides/serialization/data-types.md @@ -27,15 +27,10 @@ Mirage supports a number of data types you can use with these, including: Game Objects in SyncVars, SyncLists, and SyncDictionaries are fragile in some cases and should be used with caution. -- As long as the game object *already exists* on both the server and the client, the reference should be fine. +* **For SyncVars**: Weaver uses `NetworkIdentitySyncvar.cs` internally to manage object references automatically. Therefore, you should sync `GameObject` or `NetworkIdentity` references directly rather than manually syncing `uint` (NetId). +* **For RPCs**: RPCs are executed instantly. If the referenced game object does not yet exist on the client when the RPC payload is received (e.g. if the object hasn't spawned on the client yet or is excluded due to network visibility), passing a `GameObject` or `NetworkIdentity` reference directly will result in a null value. -When the sync data arrives at the client, the referenced game object may not yet exist on that client, resulting in null values in the sync data. This is because internally Mirage passes the `NetId` from the `NetworkIdentity` and tries to look it up on the client's `NetworkIdentity.World.Spawned` dictionary. - -If the object hasn't been spawned on the client yet, no match will be found. It could be in the same payload, especially for joining clients, but after the sync data from another object. -It could also be null because the game object is excluded from a client due to network visibility, e.g. `NetworkProximityChecker`. - -You may find that it's more robust to sync the `NetworkIdentity.NetID` (`uint`) instead, and do your own lookup in -`NetworkIdentity.World.Spawned` to get the object, perhaps in a coroutine: +For RPCs, it is more robust to pass the `NetworkIdentity.NetID` (`uint`) and perform your own lookup on the client, using `UniTask` to wait for the object to spawn: {{{ Path:'Snippets/Serialization/DataTypesSnippets.cs' Name:'game-object-lookup' }}} From 49778548904159a1be2a8900a0cd331906473fbe Mon Sep 17 00:00:00 2001 From: James Frowen Date: Tue, 2 Jun 2026 00:09:31 +0100 Subject: [PATCH 28/41] fix compile error --- .../Mirage.Analyzers/MirageAnalyzer.Lifecycles.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.Lifecycles.cs b/CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.Lifecycles.cs index ad0a054b885..639c7ff76de 100644 --- a/CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.Lifecycles.cs +++ b/CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.Lifecycles.cs @@ -33,7 +33,7 @@ private static void AnalyzeMethodDeclaration(SyntaxNodeAnalysisContext context) // MIRAGE1402: Missing base Call in OnSerialize/OnDeserialize if (methodSymbol.IsOverride && (methodSymbol.Name == "OnSerialize" || methodSymbol.Name == "OnDeserialize")) { - bool baseHierarchyHasSyncState = false; + var baseHierarchyHasSyncState = false; var baseType = containingType.BaseType; while (baseType != null && !MirageTypes.NetworkBehaviour.Is(baseType) && baseType.ToDisplayString() != "object") { @@ -94,7 +94,7 @@ private void CheckNode(ExpressionSyntax node) if (symbol is IMethodSymbol methodSymbol) { - if (MirageAttributes.ServerRpc.Has(methodSymbol) || + if (MirageAttributes.ServerRpc.Has(methodSymbol) || MirageAttributes.ClientRpc.Has(methodSymbol) || MirageAttributes.Server.Has(methodSymbol) || MirageAttributes.Client.Has(methodSymbol) || @@ -128,6 +128,7 @@ private void CheckNode(ExpressionSyntax node) } } } + } private class BaseCallWalker : CSharpSyntaxWalker { From 0a9742b79fcc6face545d9f888b97c724a46c630 Mon Sep 17 00:00:00 2001 From: James Frowen Date: Tue, 2 Jun 2026 14:00:25 +0100 Subject: [PATCH 29/41] fixing compile errors --- .../Samples~/Snippets/Analyzers/Mirage1003.cs | 2 +- .../Samples~/Snippets/Analyzers/Mirage1502.cs | 5 +++-- .../CommunityGuides/QuickStartGuide.cs | 2 +- .../GameObjects/CustomPlayerSpawning.cs | 2 +- .../GameObjects/CustomSpawnExample.cs | 8 +++---- .../GameObjects/SceneObjectFilterExample.cs | 2 +- .../Snippets/Mirage.Examples.Snippets.asmdef | 21 +++++++++++++++++++ .../Mirage.Examples.Snippets.asmdef.meta | 7 +++++++ .../RpcExampleChangeNameGenerated.cs | 2 +- .../Snippets/RemoteActions/ServerRpcDoor.cs | 2 +- .../SceneLoading/CustomSceneManager.cs | 2 +- .../SceneLoading/LoadSceneAdditivelyPlayer.cs | 6 +++--- 12 files changed, 45 insertions(+), 16 deletions(-) create mode 100644 Assets/Mirage/Samples~/Snippets/Mirage.Examples.Snippets.asmdef create mode 100644 Assets/Mirage/Samples~/Snippets/Mirage.Examples.Snippets.asmdef.meta diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1003.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1003.cs index b12ef489cc6..f5f9ece2607 100644 --- a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1003.cs +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1003.cs @@ -6,7 +6,7 @@ namespace Mirage.Snippets.Analyzers namespace M1003.Triggering { // CodeEmbed-Start: mirage1003-triggering - public struct PlayerData + public class PlayerData { public int health; } diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1502.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1502.cs index 904807b275c..ecea10b5ea1 100644 --- a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1502.cs +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1502.cs @@ -21,9 +21,10 @@ namespace M1502.Resolved [NetworkMessage] public struct ChatMessage { - // Correct: Restrict the maximum string length using BitCount or other validation attributes - [BitCount(8)] + // Correct: Restrict the maximum string length using custom validation or setting MaxStringLength +#pragma warning disable MIRAGE1502 public string text; +#pragma warning restore MIRAGE1502 } // CodeEmbed-End: mirage1502-resolved } diff --git a/Assets/Mirage/Samples~/Snippets/CommunityGuides/QuickStartGuide.cs b/Assets/Mirage/Samples~/Snippets/CommunityGuides/QuickStartGuide.cs index 3a0127cf0f1..36458bec475 100644 --- a/Assets/Mirage/Samples~/Snippets/CommunityGuides/QuickStartGuide.cs +++ b/Assets/Mirage/Samples~/Snippets/CommunityGuides/QuickStartGuide.cs @@ -55,7 +55,7 @@ namespace QuickStart public class SceneScript : NetworkBehaviour { public Text canvasStatusText; - public PlayerScript playerScript; + public PlayerScriptPart11 playerScript; [SyncVar(hook = nameof(OnStatusTextChanged))] public string statusText { get; set; } diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/CustomPlayerSpawning.cs b/Assets/Mirage/Samples~/Snippets/GameObjects/CustomPlayerSpawning.cs index 02f84d9b00b..bf435fcbc14 100644 --- a/Assets/Mirage/Samples~/Snippets/GameObjects/CustomPlayerSpawning.cs +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/CustomPlayerSpawning.cs @@ -144,7 +144,7 @@ public void Respawn(NetworkPlayer player, GameObject newPrefab) ServerObjectManager.ReplaceCharacter(player, newCharacter, keepAuthority: true); // Remove the previous character object that's now been replaced - Server.Destroy(oldPlayer); + ServerObjectManager.Destroy(oldPlayer); } } // CodeEmbed-End: custom-character-spawner-respawn diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/CustomSpawnExample.cs b/Assets/Mirage/Samples~/Snippets/GameObjects/CustomSpawnExample.cs index 4cece8a294b..cd3f5c18165 100644 --- a/Assets/Mirage/Samples~/Snippets/GameObjects/CustomSpawnExample.cs +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/CustomSpawnExample.cs @@ -6,7 +6,7 @@ namespace Mirage.Snippets.GameObjects public class CustomSpawnExample : MonoBehaviour { public ClientObjectManager ClientObjectManager; - public NetworkServer NetworkServer; + public ServerObjectManager ServerObjectManager; public NetworkIdentity coin; public NetworkIdentity m_CoinPrefab; public NetworkIdentity prefab; @@ -51,14 +51,14 @@ public void SpawnOnServer() // spawn a coin - SpawnCoin is called on client // pass in coinHash so that it is set on the Identity before it is sent to client - NetworkServer.Spawn(gameObject, coinHash); + ServerObjectManager.Spawn(gameObject, coinHash); // CodeEmbed-End: spawn-on-server } // CodeEmbed-Start: spawn-coin-methods public NetworkIdentity SpawnCoin(SpawnMessage msg) { - return Instantiate(m_CoinPrefab, msg.position, msg.rotation); + return Instantiate(m_CoinPrefab, msg.SpawnValues.Position ?? m_CoinPrefab.transform.position, msg.SpawnValues.Rotation ?? m_CoinPrefab.transform.rotation); } public void UnSpawnCoin(NetworkIdentity spawned) { @@ -75,7 +75,7 @@ void ClientConnected() // used by clientObjectManager.RegisterPrefab NetworkIdentity PoolSpawnHandler(SpawnMessage msg) { - return GetFromPool(msg.position, msg.rotation); + return GetFromPool(msg.SpawnValues.Position ?? prefab.transform.position, msg.SpawnValues.Rotation ?? prefab.transform.rotation); } // used by clientObjectManager.RegisterPrefab diff --git a/Assets/Mirage/Samples~/Snippets/GameObjects/SceneObjectFilterExample.cs b/Assets/Mirage/Samples~/Snippets/GameObjects/SceneObjectFilterExample.cs index 9c137c68612..a26e4e2d1e3 100644 --- a/Assets/Mirage/Samples~/Snippets/GameObjects/SceneObjectFilterExample.cs +++ b/Assets/Mirage/Samples~/Snippets/GameObjects/SceneObjectFilterExample.cs @@ -20,7 +20,7 @@ public void SetScene(Scene scene) void Awake() { // Set the filter before spawning scene objects - var filter = (NetworkIdentity identity) => + System.Func filter = (NetworkIdentity identity) => { return identity.gameObject.scene == myScene; }; diff --git a/Assets/Mirage/Samples~/Snippets/Mirage.Examples.Snippets.asmdef b/Assets/Mirage/Samples~/Snippets/Mirage.Examples.Snippets.asmdef new file mode 100644 index 00000000000..bef5cd18173 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Mirage.Examples.Snippets.asmdef @@ -0,0 +1,21 @@ +{ + "name": "Mirage.Examples.Snippets", + "rootNamespace": "", + "references": [ + "GUID:30817c1a0e6d646d99c048fc403f5979", + "GUID:f51ebe6a0ceec4240a699833d6309b23", + "GUID:96f081f4a0d214ee39e3aa34e9d43109", + "GUID:c0b2064c294eb174c9f3f7da398eb677", + "GUID:db69876fcdb4de041b3adaeefa87b6a6", + "GUID:bc6737b19bee94a798a182a84b660226" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/Mirage/Samples~/Snippets/Mirage.Examples.Snippets.asmdef.meta b/Assets/Mirage/Samples~/Snippets/Mirage.Examples.Snippets.asmdef.meta new file mode 100644 index 00000000000..18e05d5f8a6 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Mirage.Examples.Snippets.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 10048530c22cf7746a4aef495db64782 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/RemoteActions/RpcExampleChangeNameGenerated.cs b/Assets/Mirage/Samples~/Snippets/RemoteActions/RpcExampleChangeNameGenerated.cs index e0983d25faa..3502d4ec33d 100644 --- a/Assets/Mirage/Samples~/Snippets/RemoteActions/RpcExampleChangeNameGenerated.cs +++ b/Assets/Mirage/Samples~/Snippets/RemoteActions/RpcExampleChangeNameGenerated.cs @@ -2,10 +2,10 @@ using Mirage; using Mirage.Serialization; using Mirage.RemoteCalls; -using NetworkBehaviour = Mirage.Snippets.RemoteActions.RpcExamplesGenerated.DummyNetworkBehaviour; namespace Mirage.Snippets.RemoteActions.RpcExamplesGenerated { + using NetworkBehaviour = DummyNetworkBehaviour; // Dummy classes/aliases to make the snippet code compile in Unity public class DummyNetworkBehaviour { diff --git a/Assets/Mirage/Samples~/Snippets/RemoteActions/ServerRpcDoor.cs b/Assets/Mirage/Samples~/Snippets/RemoteActions/ServerRpcDoor.cs index aaa52dfcdc5..2486eecf49e 100644 --- a/Assets/Mirage/Samples~/Snippets/RemoteActions/ServerRpcDoor.cs +++ b/Assets/Mirage/Samples~/Snippets/RemoteActions/ServerRpcDoor.cs @@ -28,7 +28,7 @@ public class Door : NetworkBehaviour [ServerRpc(requireAuthority = false)] public void CmdSetDoorState(DoorState newDoorState, INetworkPlayer sender = null) { - if (sender.identity.GetComponent().hasDoorKey) + if (sender.Identity.GetComponent().hasDoorKey) doorState = newDoorState; } } diff --git a/Assets/Mirage/Samples~/Snippets/SceneLoading/CustomSceneManager.cs b/Assets/Mirage/Samples~/Snippets/SceneLoading/CustomSceneManager.cs index e6b6d1c698f..6fb52ac7ef5 100644 --- a/Assets/Mirage/Samples~/Snippets/SceneLoading/CustomSceneManager.cs +++ b/Assets/Mirage/Samples~/Snippets/SceneLoading/CustomSceneManager.cs @@ -4,7 +4,7 @@ namespace Mirage.Snippets.SceneLoading.OverrideOnServerAuthenticated // CodeEmbed-Start: custom-scene-manager-on-server-authenticated public class MySceneManager : NetworkSceneManager { - protected internal override void OnServerAuthenticated(INetworkPlayer player) + protected override void OnServerAuthenticated(INetworkPlayer player) { // just load server's active scene instead of all additive scenes as well player.Send(new SceneMessage { MainActivateScene = ActiveScenePath }); diff --git a/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneAdditivelyPlayer.cs b/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneAdditivelyPlayer.cs index 86b612fb34e..2102a525bb6 100644 --- a/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneAdditivelyPlayer.cs +++ b/Assets/Mirage/Samples~/Snippets/SceneLoading/LoadSceneAdditivelyPlayer.cs @@ -8,21 +8,21 @@ public class LoadSceneAdditivelyPlayer : MonoBehaviour public void Example(NetworkSceneManager sceneManager, INetworkPlayer Player) { // CodeEmbed-Start: load-scene-additively-player - sceneManager.ServerLoadSceneAdditively("path to scene asset file.", Player); + sceneManager.ServerLoadSceneAdditively("path to scene asset file.", new[] { Player }); // CodeEmbed-End: load-scene-additively-player } public void ExampleNormal(NetworkSceneManager sceneManager, INetworkPlayer Player) { // CodeEmbed-Start: load-scene-additively-player-normal - sceneManager.ServerLoadSceneAdditively("path to scene asset file.", Player, true); + sceneManager.ServerLoadSceneAdditively("path to scene asset file.", new[] { Player }, true); // CodeEmbed-End: load-scene-additively-player-normal } public void ExamplePhysics(NetworkSceneManager sceneManager, INetworkPlayer Player) { // CodeEmbed-Start: load-scene-additively-player-physics - sceneManager.ServerLoadSceneAdditively("path to scene asset file.", Player, false, new UnityEngine.SceneManagement.LoadSceneParameters { loadSceneMode = UnityEngine.SceneManagement.LoadSceneMode.Additive, localPhysicsMode = UnityEngine.SceneManagement.LocalPhysicsMode.Physics2D }); + sceneManager.ServerLoadSceneAdditively("path to scene asset file.", new[] { Player }, false, new UnityEngine.SceneManagement.LoadSceneParameters { loadSceneMode = UnityEngine.SceneManagement.LoadSceneMode.Additive, localPhysicsMode = UnityEngine.SceneManagement.LocalPhysicsMode.Physics2D }); // CodeEmbed-End: load-scene-additively-player-physics } } From d1ea6f64d6a48d0c6b021b9b306d9a8eaa4ffb2c Mon Sep 17 00:00:00 2001 From: James Frowen Date: Tue, 2 Jun 2026 14:14:57 +0100 Subject: [PATCH 30/41] roadmap --- CSharpAnalyzers/analyzers_roadmap.md | 64 ++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 CSharpAnalyzers/analyzers_roadmap.md diff --git a/CSharpAnalyzers/analyzers_roadmap.md b/CSharpAnalyzers/analyzers_roadmap.md new file mode 100644 index 00000000000..85a545d5a6a --- /dev/null +++ b/CSharpAnalyzers/analyzers_roadmap.md @@ -0,0 +1,64 @@ +# Mirage Roslyn Analyzers Roadmap & Checklist (Updated) + +This document tracks the implementation status of the proposed Roslyn Analyzers for Mirage, updated with user feedback to avoid gaps and adjust severities/rules. + +--- + +## Group 1: SyncVars & SyncObjects (`MIRAGE1000 – MIRAGE1099`) + +* [ ] **`MIRAGE1001`**: Warning: SyncVar Class Warning (Warns on class-based SyncVars / SyncObject type arguments, including types like List, T[], Dictionary). +* [ ] **`MIRAGE1002`**: Error: SyncVar Auto-Property Error (Requires SyncVar properties to be non-static auto-properties with both get/set). +* [ ] **`MIRAGE1003`**: Warning: Direct Mutation of SyncCollection Elements (Warning when modifying fields of class elements inside a `SyncList`/`SyncDictionary` without calling `SetItemDirty`). (ignore sync-by-reference like NetworkIdentity/Behaviour) +* [ ] **`MIRAGE1004`**: Error: Reassignment of SyncObject Fields, error when not assigned and not readonly (Error when `ISyncObject` fields like `SyncList` are reassigned or not marked `readonly`). +* [ ] **`MIRAGE1005`**: Error: Invalid SyncVar Hook Method (Error when a SyncVar hook method does not exist in the class, is static, or does not match the signature `void Hook(T oldValue, T newValue)`). **IMPORTANT TODO: check real syncvar hook rules from weaver code, they can be methods or events** + +--- + +## Group 2: Network Behaviour & Attribute Placement (`MIRAGE1100 – MIRAGE1199`) + +* [ ] **`MIRAGE1101`**: Error: Misplaced Network Attribute Error (Verifies network attributes are only on classes inheriting from `NetworkBehaviour`). +* [ ] **`MIRAGE1102`**: Warning: Redundant Server/Client Attribute on RPC (Warning if a method has both `[ServerRpc]` and `[Server]` or `[ClientRpc]` and `[Client]`). + +--- + +## Group 3: Remote Procedure Calls (`MIRAGE1200 – MIRAGE1299`) + +* [ ] **`MIRAGE1201`**: Warning: NetworkMessage/RPC Class Warning (Warns on class-based fields/parameters, ignoring types like List, T[], Dictionary). +* [ ] **`MIRAGE1202`**: Error: RPC Signature Error, generic allowed, void allowed, `UniTask` (with return value) allowed, `UniTask` (not return value) not allowed. +* [ ] **`MIRAGE1203`**: Error: Pass-by-Reference Modifiers in RPCs (Error if parameters use `ref`, `in`, or `out`). +* [ ] **`MIRAGE1204`**: Error: Static RPC Methods (Error if an RPC method is marked `static`). +* [ ] **`MIRAGE1205`**: Error: Invalid ClientRpc Target Configurations, if using RpcTarget.Player, first Argument should be INetworkPlayer. +* [ ] **`MIRAGE1206`**: Error: Invalid RateLimit Attribute Settings (Error on zero/negative values inside `[RateLimit]`). +* [ ] **`MIRAGE1207`**: Warning: Missing RateLimit on ServerRpc (Warning if a `[ServerRpc]` method is missing the `[RateLimit]` attribute, as all client->server messages should be limited). + +--- + +## Group 4: Serialization (`MIRAGE1300 – MIRAGE1399`) + +* [ ] **`MIRAGE1301`**: Error: Field Type Serialization Validation (Error if type inside `[NetworkMessage]` or RPC is not serializable and has no custom serializer). +* [ ] **`MIRAGE1302`**: (SKIP: this will be added later when we have source gen instead of Weaver dll edit) Warning: Field Type Serialization Validation, private fields or properties that will not be serialized (eg, structs sent over the network should be clean and minimal to avoid developer confusion). +* [ ] **`MIRAGE1303`**: Error: Mismatched Custom Serialization Methods (Error if custom writer `Write` exists without matching reader `Read` or vice-versa - implemented as Error to prevent Weaver compile errors). +* [ ] **`MIRAGE1304`**: Error: Non-Serializable MonoBehaviour Parameter (Error if RPC parameter or NetworkMessage field is a `MonoBehaviour` type that does not inherit from `NetworkBehaviour`). +* [ ] **`MIRAGE1305`**: Warning: Missing `[NetworkMessage]` Attribute (Warning if a type is sent or registered as a message, but is missing the `[NetworkMessage]` attribute). + +--- + +## Group 5: General API & Lifecycles (`MIRAGE1400 – MIRAGE1499`) + +* [ ] **`MIRAGE1401`**: Accessing Network State in Awake/Start (Warning if checking `IsServer`/`IsClient`, accessing network behavior/identity properties, or calling RPCs inside `Awake()` or `Start()`). +* [ ] **`MIRAGE1402`**: Missing base Call in OnSerialize/OnDeserialize (Warning if overrides do not call base implementations in components containing SyncVars or SyncObjects). + +--- + +## Group 6: Network Performance & Size Estimation (`MIRAGE1500 – MIRAGE1599`) +*Note: These rules are designated as `DiagnosticSeverity.Warning` to warn about security/performance issues, and output a parser-friendly format (e.g. JSON-like summary of size calculations) to facilitate future CodeLens or editor tooling integration.* + +* [ ] **`MIRAGE1501`**: Network Message Exceeds Safe MTU (Warning if estimated serialization size exceeds 1200 bytes). +* [ ] **`MIRAGE1502`**: Unbounded String or Collection (Warning if string/collection has no defined size bounds). +* [ ] **`MIRAGE1503`**: High Bit-Over-Head Primitive Type (Warning on uncompressed float/vector transfers). + +--- + +## Extra notes +### Classes for network syncing +note on MIRAGE1001, MIRAGE1201. Warnings for class being sent over network, if the target object is a struct, we should check its field and apply these same rules. we should list the warning on the Syncvar/rpc/networkMessage if its field or subfields break the rule. From f5fd1a8f751c0ba47589d7963b45bc1b523f7417 Mon Sep 17 00:00:00 2001 From: James Frowen Date: Tue, 2 Jun 2026 14:30:46 +0100 Subject: [PATCH 31/41] docs --- .../Samples~/Snippets/Analyzers/Mirage1005.cs | 78 +++++++++ .../Samples~/Snippets/Analyzers/Mirage1102.cs | 48 ++++++ .../Samples~/Snippets/Analyzers/Mirage1201.cs | 76 +++++--- .../Samples~/Snippets/Analyzers/Mirage1202.cs | 27 ++- .../Samples~/Snippets/Analyzers/Mirage1203.cs | 15 +- .../Samples~/Snippets/Analyzers/Mirage1204.cs | 27 +-- .../Samples~/Snippets/Analyzers/Mirage1205.cs | 29 +++- .../Samples~/Snippets/Analyzers/Mirage1206.cs | 11 +- .../Samples~/Snippets/Analyzers/Mirage1207.cs | 33 ++++ .../Samples~/Snippets/Analyzers/Mirage1301.cs | 60 ++----- .../Snippets/Analyzers/Mirage1301.cs.meta | 11 -- .../Samples~/Snippets/Analyzers/Mirage1302.cs | 15 +- .../Snippets/Analyzers/Mirage1302.cs.meta | 11 -- .../Samples~/Snippets/Analyzers/Mirage1304.cs | 39 +++++ .../Samples~/Snippets/Analyzers/Mirage1305.cs | 43 +++++ .../Samples~/Snippets/Analyzers/Mirage1401.cs | 2 + .../Samples~/Snippets/Analyzers/Mirage1501.cs | 16 +- .../Samples~/Snippets/Analyzers/Mirage1502.cs | 5 +- .../Samples~/Snippets/Analyzers/Mirage1503.cs | 1 + CSharpAnalyzers/analyzers_research_report.md | 162 ++++++++++++++++++ CSharpAnalyzers/analyzers_roadmap.md | 54 +++--- doc/docs/analyzers/MIRAGE1005.md | 28 +++ doc/docs/analyzers/MIRAGE1101.md | 8 +- doc/docs/analyzers/MIRAGE1102.md | 17 ++ doc/docs/analyzers/MIRAGE1201.md | 25 ++- doc/docs/analyzers/MIRAGE1202.md | 11 +- doc/docs/analyzers/MIRAGE1203.md | 8 +- doc/docs/analyzers/MIRAGE1204.md | 11 +- doc/docs/analyzers/MIRAGE1205.md | 14 +- doc/docs/analyzers/MIRAGE1206.md | 11 +- doc/docs/analyzers/MIRAGE1207.md | 19 ++ doc/docs/analyzers/MIRAGE1301.md | 22 +-- doc/docs/analyzers/MIRAGE1302.md | 10 +- doc/docs/analyzers/MIRAGE1304.md | 21 +++ doc/docs/analyzers/MIRAGE1305.md | 19 ++ doc/docs/analyzers/MIRAGE1401.md | 3 +- doc/docs/analyzers/index.md | 58 +++++-- 37 files changed, 782 insertions(+), 266 deletions(-) create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1005.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1102.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1207.cs delete mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1301.cs.meta delete mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1302.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1304.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1305.cs create mode 100644 CSharpAnalyzers/analyzers_research_report.md create mode 100644 doc/docs/analyzers/MIRAGE1005.md create mode 100644 doc/docs/analyzers/MIRAGE1102.md create mode 100644 doc/docs/analyzers/MIRAGE1207.md create mode 100644 doc/docs/analyzers/MIRAGE1304.md create mode 100644 doc/docs/analyzers/MIRAGE1305.md diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1005.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1005.cs new file mode 100644 index 00000000000..26d1a0e9165 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1005.cs @@ -0,0 +1,78 @@ +using System; +using Mirage; + +namespace Mirage.Snippets.Analyzers +{ + namespace M1005.Triggering + { + // CodeEmbed-Start: mirage1005-triggering + public class Player : NetworkBehaviour + { + // Case 1: Hook method 'OnHealthChanged' does not exist in the class + [SyncVar(hook = "OnHealthChanged")] + public int health { get; set; } + + // Case 2: Hook method parameter types do not match the SyncVar's type (int vs float) + [SyncVar(hook = nameof(OnManaChanged))] + public int mana { get; set; } + + public void OnManaChanged(float oldMana, float newMana) + { + // Wrong parameter types + } + + // Case 3: A static event hook is declared (unsupported, causes invalid IL in weaver) + [SyncVar(hook = nameof(OnScoreChanged))] + public int score { get; set; } + + public static event Action OnScoreChanged; + + // Case 4: Multiple matching overloads exist under automatic hook type resolving + [SyncVar(hook = nameof(OnGoldChanged))] + public int gold { get; set; } + + public void OnGoldChanged() { } + public void OnGoldChanged(int oldGold, int newGold) { } + } + // CodeEmbed-End: mirage1005-triggering + } + + namespace M1005.Resolved + { + // CodeEmbed-Start: mirage1005-resolved + public class Player : NetworkBehaviour + { + // Case 1: Hook method is an instance method with 2 parameters matching the type exactly + [SyncVar(hook = nameof(OnHealthChanged))] + public int health { get; set; } + + public void OnHealthChanged(int oldHealth, int newHealth) + { + // Correct instance method hook + } + + // Case 2: Hook method is a static method (fully supported by Mirage Weaver) + [SyncVar(hook = nameof(OnManaChanged))] + public int mana { get; set; } + + public static void OnManaChanged(int oldMana, int newMana) + { + // Correct static method hook + } + + // Case 3: Hook can be an instance event of type System.Action + [SyncVar(hook = nameof(OnScoreChanged))] + public int score { get; set; } + + public event Action OnScoreChanged; + + // Case 4: Multiple overloads resolved by explicitly specifying hookType + [SyncVar(hook = nameof(OnGoldChanged), hookType = SyncHookType.MethodWith2Arg)] + public int gold { get; set; } + + public void OnGoldChanged() { } + public void OnGoldChanged(int oldGold, int newGold) { } + } + // CodeEmbed-End: mirage1005-resolved + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1102.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1102.cs new file mode 100644 index 00000000000..a8bcb1b2b3f --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1102.cs @@ -0,0 +1,48 @@ +using Mirage; + +namespace Mirage.Snippets.Analyzers +{ + namespace M1102.Triggering + { + // CodeEmbed-Start: mirage1102-triggering + public class PlayerCombat : NetworkBehaviour + { + // Redundant: [Server] is redundant on a [ServerRpc] method + [Server] + [ServerRpc] + public void CmdFireWeapon(int weaponId) + { + // Weapon fire logic + } + + // Redundant: [Client] is redundant on a [ClientRpc] method + [Client] + [ClientRpc] + public void RpcPlayExplosion(UnityEngine.Vector3 position) + { + // Play explosion effect + } + } + // CodeEmbed-End: mirage1102-triggering + } + + namespace M1102.Resolved + { + // CodeEmbed-Start: mirage1102-resolved + public class PlayerCombat : NetworkBehaviour + { + [ServerRpc] + public void CmdFireWeapon(int weaponId) + { + // Weapon fire logic + } + + [ClientRpc] + public void RpcPlayExplosion(UnityEngine.Vector3 position) + { + // Play explosion effect + } + } + // CodeEmbed-End: mirage1102-resolved + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1201.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1201.cs index b9c07e0ea93..b908fe2e1f3 100644 --- a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1201.cs +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1201.cs @@ -1,48 +1,76 @@ using Mirage; -using Cysharp.Threading.Tasks; +using Mirage.Serialization; namespace Mirage.Snippets.Analyzers { - public struct PlayerStats {} - namespace M1201.Triggering { // CodeEmbed-Start: mirage1201-triggering - public class Player : NetworkBehaviour + [NetworkMessage] + public struct UpdateUserMessage { - // Errors: RPC method 'CmdTakeDamage' is invalid: cannot have generic parameters. - [ServerRpc] - public void CmdTakeDamage(T damage) - { - } + // Warning: Class types cause GC allocations during deserialization and lack change tracking. + public UserData data; + } - // Errors: RPC method 'CmdGetStats' is invalid: cannot return 'PlayerStats'... - [ServerRpc] - public PlayerStats CmdGetStats() - { - return new PlayerStats(); - } + public class UserData + { + public string name; } // CodeEmbed-End: mirage1201-triggering } namespace M1201.Resolved { - // CodeEmbed-Start: mirage1201-resolved - public class Player : NetworkBehaviour + // CodeEmbed-Start: mirage1201-struct-option + [NetworkMessage] + public struct UpdateUserMessage + { + public UserDataStruct data; + } + + // Correct: Structs avoid memory allocation issues on deserialization. + public struct UserDataStruct + { + public string name; + } + // CodeEmbed-End: mirage1201-struct-option + + // CodeEmbed-Start: mirage1201-class-option + // Correct: WeaverSafeClass confirms custom serialization handles allocations safely. + [WeaverSafeClass] + public class UserDataClass + { + public string name; + } + + public static class UserDataSerializer { - [ServerRpc] - public void CmdTakeDamage(int damage) + public static void WriteUserData(this NetworkWriter writer, UserDataClass data) { + writer.WriteString(data.name); } - [ServerRpc] - public async UniTask CmdGetStats() + public static UserDataClass ReadUserData(this NetworkReader reader) { - await UniTask.Yield(); - return new PlayerStats(); + return new UserDataClass { name = reader.ReadString() }; } } - // CodeEmbed-End: mirage1201-resolved + // CodeEmbed-End: mirage1201-class-option + + // CodeEmbed-Start: mirage1201-suppress-option + [NetworkMessage] + public struct UpdateUserMessageWithSuppressed + { + // Correct: WeaverSafeClass on individual members overrides validation warnings. + [WeaverSafeClass] + public UserDataClassWithoutAttribute data; + } + + public class UserDataClassWithoutAttribute + { + public string name; + } + // CodeEmbed-End: mirage1201-suppress-option } } diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1202.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1202.cs index 0a7d4d645ff..7e679bec53e 100644 --- a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1202.cs +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1202.cs @@ -1,17 +1,26 @@ using Mirage; +using Cysharp.Threading.Tasks; namespace Mirage.Snippets.Analyzers { + public struct PlayerStats {} + namespace M1202.Triggering { // CodeEmbed-Start: mirage1202-triggering public class Player : NetworkBehaviour { - // Error: ServerRpc method 'CmdTakeDamage' cannot have ref/out parameters + // Errors: RPC method 'CmdTakeDamage' is invalid: cannot have generic parameters. + [ServerRpc] + public void CmdTakeDamage(T damage) + { + } + + // Errors: RPC method 'CmdGetStats' is invalid: cannot return 'PlayerStats' (must return void or UniTask). [ServerRpc] - public void CmdTakeDamage(ref int health) + public PlayerStats CmdGetStats() { - health -= 10; + return new PlayerStats(); } } // CodeEmbed-End: mirage1202-triggering @@ -22,14 +31,16 @@ namespace M1202.Resolved // CodeEmbed-Start: mirage1202-resolved public class Player : NetworkBehaviour { - [SyncVar] - public int Health { get; set; } - - // Correct: Pass by value and synchronize via SyncVar [ServerRpc] public void CmdTakeDamage(int damage) { - Health -= damage; + } + + [ServerRpc] + public async UniTask CmdGetStats() + { + await UniTask.Yield(); + return new PlayerStats(); } } // CodeEmbed-End: mirage1202-resolved diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1203.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1203.cs index ecc207d388c..a6178722437 100644 --- a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1203.cs +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1203.cs @@ -7,11 +7,11 @@ namespace M1203.Triggering // CodeEmbed-Start: mirage1203-triggering public class Player : NetworkBehaviour { - // Error: ServerRpc method 'CmdSpawnGlobal' must not be static + // Error: ServerRpc method 'CmdTakeDamage' cannot have ref/out parameters [ServerRpc] - public static void CmdSpawnGlobal() + public void CmdTakeDamage(ref int health) { - // Static context has no NetworkIdentity + health -= 10; } } // CodeEmbed-End: mirage1203-triggering @@ -22,11 +22,14 @@ namespace M1203.Resolved // CodeEmbed-Start: mirage1203-resolved public class Player : NetworkBehaviour { - // Correct: Instance method has access to the NetworkBehaviour state + [SyncVar] + public int Health { get; set; } + + // Correct: Pass by value and synchronize via SyncVar [ServerRpc] - public void CmdSpawn() + public void CmdTakeDamage(int damage) { - // Normal instance context + Health -= damage; } } // CodeEmbed-End: mirage1203-resolved diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1204.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1204.cs index ae488d6715a..3f0e667ed29 100644 --- a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1204.cs +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1204.cs @@ -1,5 +1,4 @@ using Mirage; -using Cysharp.Threading.Tasks; namespace Mirage.Snippets.Analyzers { @@ -8,16 +7,9 @@ namespace M1204.Triggering // CodeEmbed-Start: mirage1204-triggering public class Player : NetworkBehaviour { - // Error: [ClientRpc] must return void when target is Observers. - [ClientRpc(target = RpcTarget.Observers)] - public UniTask RpcGetHealth() - { - return UniTask.FromResult(100); - } - - // Error: ClientRpc method with target = Player requires first parameter to be INetworkPlayer - [ClientRpc(target = RpcTarget.Player)] - public void RpcGiveItem(int itemId) + // Error: ServerRpc method 'CmdSpawnGlobal' must not be static + [ServerRpc] + public static void CmdSpawnGlobal() { } } @@ -29,16 +21,9 @@ namespace M1204.Resolved // CodeEmbed-Start: mirage1204-resolved public class Player : NetworkBehaviour { - // Correct: Targeted RPC returning value to the Owner - [ClientRpc(target = RpcTarget.Owner)] - public UniTask RpcGetHealth() - { - return UniTask.FromResult(100); - } - - // Correct: First parameter is the target player connection - [ClientRpc(target = RpcTarget.Player)] - public void RpcGiveItem(INetworkPlayer targetPlayer, int itemId) + // Correct: Instance method has access to the NetworkBehaviour state + [ServerRpc] + public void CmdSpawn() { } } diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1205.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1205.cs index 326eabb1692..b082e45765a 100644 --- a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1205.cs +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1205.cs @@ -1,4 +1,5 @@ using Mirage; +using Cysharp.Threading.Tasks; namespace Mirage.Snippets.Analyzers { @@ -7,10 +8,16 @@ namespace M1205.Triggering // CodeEmbed-Start: mirage1205-triggering public class Player : NetworkBehaviour { - // Error: RateLimit interval must be greater than zero, and MaxTokens must be >= Refill - [ServerRpc] - [RateLimit(Interval = -0.5f, Refill = 10, MaxTokens = 5)] - public void CmdSpammyAction() + // Error: [ClientRpc] must return void when target is Observers. + [ClientRpc(target = RpcTarget.Observers)] + public UniTask RpcGetHealth() + { + return UniTask.FromResult(100); + } + + // Error: ClientRpc method with target = Player requires first parameter to be INetworkPlayer + [ClientRpc(target = RpcTarget.Player)] + public void RpcGiveItem(int itemId) { } } @@ -22,10 +29,16 @@ namespace M1205.Resolved // CodeEmbed-Start: mirage1205-resolved public class Player : NetworkBehaviour { - // Correct: Positive interval and MaxTokens >= Refill - [ServerRpc] - [RateLimit(Interval = 1.0f, Refill = 10, MaxTokens = 20)] - public void CmdSpammyAction() + // Correct: Targeted RPC returning value to the Owner + [ClientRpc(target = RpcTarget.Owner)] + public UniTask RpcGetHealth() + { + return UniTask.FromResult(100); + } + + // Correct: First parameter is the target player connection + [ClientRpc(target = RpcTarget.Player)] + public void RpcGiveItem(INetworkPlayer targetPlayer, int itemId) { } } diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1206.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1206.cs index 1c0f62c871c..278ddc5dc55 100644 --- a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1206.cs +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1206.cs @@ -7,9 +7,10 @@ namespace M1206.Triggering // CodeEmbed-Start: mirage1206-triggering public class Player : NetworkBehaviour { - // Warning: ServerRpc 'CmdFireWeapon' should have a [RateLimit] attribute to prevent spam + // Error: RateLimit interval must be greater than zero, and MaxTokens must be >= Refill [ServerRpc] - public void CmdFireWeapon() + [RateLimit(Interval = -0.5f, Refill = 10, MaxTokens = 5)] + public void CmdSpammyAction() { } } @@ -21,10 +22,10 @@ namespace M1206.Resolved // CodeEmbed-Start: mirage1206-resolved public class Player : NetworkBehaviour { - // Correct: ServerRpc decorated with [RateLimit] to throttle client requests + // Correct: Positive interval and MaxTokens >= Refill [ServerRpc] - [RateLimit(Interval = 0.2f, Refill = 5, MaxTokens = 10)] - public void CmdFireWeapon() + [RateLimit(Interval = 1.0f, Refill = 10, MaxTokens = 20)] + public void CmdSpammyAction() { } } diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1207.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1207.cs new file mode 100644 index 00000000000..f8d9586f2af --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1207.cs @@ -0,0 +1,33 @@ +using Mirage; + +namespace Mirage.Snippets.Analyzers +{ + namespace M1207.Triggering + { + // CodeEmbed-Start: mirage1207-triggering + public class Player : NetworkBehaviour + { + // Warning: ServerRpc 'CmdFireWeapon' should have a [RateLimit] attribute to prevent spam + [ServerRpc] + public void CmdFireWeapon() + { + } + } + // CodeEmbed-End: mirage1207-triggering + } + + namespace M1207.Resolved + { + // CodeEmbed-Start: mirage1207-resolved + public class Player : NetworkBehaviour + { + // Correct: ServerRpc decorated with [RateLimit] to throttle client requests + [ServerRpc] + [RateLimit(Interval = 0.2f, Refill = 5, MaxTokens = 10)] + public void CmdFireWeapon() + { + } + } + // CodeEmbed-End: mirage1207-resolved + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1301.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1301.cs index 41f782af186..d4396634b96 100644 --- a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1301.cs +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1301.cs @@ -1,69 +1,29 @@ using Mirage; +using System.Threading; namespace Mirage.Snippets.Analyzers { namespace M1301.Triggering { // CodeEmbed-Start: mirage1301-triggering - public class TargetInfo - { - public int x; - public int y; - } - [NetworkMessage] - public struct FireMessage + public struct StartSessionMessage { - // Warns: NetworkMessage field 'info' is a class type 'TargetInfo'. - public TargetInfo info; + // Error: Field type 'Thread' is not serializable by Mirage. + public Thread executionThread; } // CodeEmbed-End: mirage1301-triggering } - namespace M1301.StructOption - { - // CodeEmbed-Start: mirage1301-struct-option - public struct TargetInfo - { - public int x; - public int y; - } - - [NetworkMessage] - public struct FireMessage - { - public TargetInfo info; - } - // CodeEmbed-End: mirage1301-struct-option - } - - namespace M1301.ClassOption - { - // CodeEmbed-Start: mirage1301-class-option - [WeaverSafeClass] - public class TargetInfo - { - public int x; - public int y; - } - // CodeEmbed-End: mirage1301-class-option - } - - namespace M1301.SuppressOption + namespace M1301.Resolved { - // CodeEmbed-Start: mirage1301-suppress-option + // CodeEmbed-Start: mirage1301-resolved [NetworkMessage] - public struct FireMessage - { - [WeaverSafeClass] - public TargetInfo info; - } - // CodeEmbed-End: mirage1301-suppress-option - - public class TargetInfo + public struct StartSessionMessage { - public int x; - public int y; + // Correct: Pass a serializable identifier instead of the raw thread object + public string threadName; } + // CodeEmbed-End: mirage1301-resolved } } diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1301.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1301.cs.meta deleted file mode 100644 index 706875fd625..00000000000 --- a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1301.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: adef096b2cf08ab48968cff17de54308 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1302.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1302.cs index 15b12adaf63..dd140094dd7 100644 --- a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1302.cs +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1302.cs @@ -1,5 +1,4 @@ using Mirage; -using System.Threading; namespace Mirage.Snippets.Analyzers { @@ -7,10 +6,12 @@ namespace M1302.Triggering { // CodeEmbed-Start: mirage1302-triggering [NetworkMessage] - public struct StartSessionMessage + public struct StatusMessage { - // Error: Field type 'Thread' is not serializable by Mirage. - public Thread executionThread; + public string playerName; + + // Warning: Private fields are not serialized by the Weaver + private int playerHash; } // CodeEmbed-End: mirage1302-triggering } @@ -19,10 +20,10 @@ namespace M1302.Resolved { // CodeEmbed-Start: mirage1302-resolved [NetworkMessage] - public struct StartSessionMessage + public struct StatusMessage { - // Correct: Pass a serializable identifier instead of the raw thread object - public string threadName; + public string playerName; + public int playerHash; } // CodeEmbed-End: mirage1302-resolved } diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1302.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1302.cs.meta deleted file mode 100644 index 651c1181b94..00000000000 --- a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1302.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: f17fe3396d839284c995fd290652bab6 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1304.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1304.cs new file mode 100644 index 00000000000..5b45377edea --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1304.cs @@ -0,0 +1,39 @@ +using Mirage; +using UnityEngine; + +namespace Mirage.Snippets.Analyzers +{ + namespace M1304.Triggering + { + // CodeEmbed-Start: mirage1304-triggering + public class LocalComponent : MonoBehaviour + { + public int health; + } + + [NetworkMessage] + public struct DamageMessage + { + // Error: MonoBehaviour type 'LocalComponent' is not serializable by Mirage. + public LocalComponent target; + } + // CodeEmbed-End: mirage1304-triggering + } + + namespace M1304.Resolved + { + // CodeEmbed-Start: mirage1304-resolved + public class NetworkedComponent : NetworkBehaviour + { + public int health; + } + + [NetworkMessage] + public struct DamageMessage + { + // Correct: NetworkBehaviour is serializable by Mirage + public NetworkedComponent target; + } + // CodeEmbed-End: mirage1304-resolved + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1305.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1305.cs new file mode 100644 index 00000000000..454d57e5860 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1305.cs @@ -0,0 +1,43 @@ +using Mirage; + +namespace Mirage.Snippets.Analyzers +{ + namespace M1305.Triggering + { + // CodeEmbed-Start: mirage1305-triggering + // Error: Lacks [NetworkMessage] attribute, but is sent/registered as a message + public struct PlayerScoreMessage + { + public int score; + } + + public class GameClient : NetworkBehaviour + { + public void NotifyScore(INetworkPlayer player, PlayerScoreMessage msg) + { + player.Send(msg); + } + } + // CodeEmbed-End: mirage1305-triggering + } + + namespace M1305.Resolved + { + // CodeEmbed-Start: mirage1305-resolved + // Correct: Message is marked with [NetworkMessage] + [NetworkMessage] + public struct PlayerScoreMessage + { + public int score; + } + + public class GameClient : NetworkBehaviour + { + public void NotifyScore(INetworkPlayer player, PlayerScoreMessage msg) + { + player.Send(msg); + } + } + // CodeEmbed-End: mirage1305-resolved + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1401.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1401.cs index 5af4b4b6d49..ccf1d7419e2 100644 --- a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1401.cs +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1401.cs @@ -1,3 +1,5 @@ +using Mirage; + namespace Mirage.Snippets.Analyzers { namespace M1401.Triggering diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1501.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1501.cs index cb678254af9..b82118df15e 100644 --- a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1501.cs +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1501.cs @@ -1,4 +1,5 @@ using Mirage; +using UnityEngine; namespace Mirage.Snippets.Analyzers { @@ -6,10 +7,10 @@ namespace M1501.Triggering { // CodeEmbed-Start: mirage1501-triggering [NetworkMessage] - public struct HugeMessage + public struct LargeTelemetryMessage { - // Warning: Array size and primitives exceed the safe MTU threshold - public byte[] largeBuffer; // e.g. filled with 2048 bytes of data + // Warning: Array of Vector3 has estimated size of 1536 bytes, exceeding the 1200-byte MTU limit. + public Vector3[] historicalTransforms; } // CodeEmbed-End: mirage1501-triggering } @@ -18,11 +19,12 @@ namespace M1501.Resolved { // CodeEmbed-Start: mirage1501-resolved [NetworkMessage] - public struct ChunkMessage + public struct TelemetryUpdateMessage { - public int chunkIndex; - // Correct: Small buffer sizes that fit comfortably within a single MTU packet - public byte[] smallBuffer; // e.g. limited to 512 bytes per chunk + public int sequenceNumber; + // Correct: Send individual updates frequently instead of a large history array. + public Vector3 position; + public Quaternion rotation; } // CodeEmbed-End: mirage1501-resolved } diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1502.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1502.cs index ecea10b5ea1..896e9b16af9 100644 --- a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1502.cs +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1502.cs @@ -21,10 +21,9 @@ namespace M1502.Resolved [NetworkMessage] public struct ChatMessage { - // Correct: Restrict the maximum string length using custom validation or setting MaxStringLength -#pragma warning disable MIRAGE1502 + // Correct: Bounded string using [BitCount] to limit length + [BitCount(8)] public string text; -#pragma warning restore MIRAGE1502 } // CodeEmbed-End: mirage1502-resolved } diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1503.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1503.cs index f17b8589362..574870601a2 100644 --- a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1503.cs +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1503.cs @@ -1,3 +1,4 @@ +using Mirage; using Mirage.Serialization; namespace Mirage.Snippets.Analyzers diff --git a/CSharpAnalyzers/analyzers_research_report.md b/CSharpAnalyzers/analyzers_research_report.md new file mode 100644 index 00000000000..d18462b5a77 --- /dev/null +++ b/CSharpAnalyzers/analyzers_research_report.md @@ -0,0 +1,162 @@ +# Mirage Roslyn Analyzer Proposal: Future Rules & Serialization Size Estimation + +This report compiles the static analysis opportunities identified by five concurrent research subagents. It includes baseline overhead calculations, size estimation rules, and proposed analyzer rules. + +--- + +## 1. Baseline Overhead & Size Estimation (Message/RPC) + +Mirage uses `NetworkWriter` (a bit-level stream writer) to pack messages. + +### Message ID Overhead +Every `[NetworkMessage]` writes a **2-byte (16-bit) Message ID** at the start of the payload. The ID is computed as: +```csharp +var id = GetId(); +writer.WriteUInt16((ushort)id); // 2 bytes +``` + +### RPC Envelope Overheads +RPCs package arguments into a custom struct payload, which is then wrapped in a standard `[NetworkMessage]` defined in `Assets/Mirage/Runtime/RemoteCalls/RpcMessages.cs`. Under typical gameplay conditions (`NetId` < 240, `FunctionIndex` < 240, `ReplyId` < 240, occupying **1 byte** each), the baseline headers are: + +1. **`RpcMessage` (Standard RPCs)** + * Message ID: 2 bytes + * `NetId` (packed uint): 1 byte (typical) + * `FunctionIndex` (packed int): 1 byte (typical) + * `Payload` Length Prefix (packed uint): 1 byte (typical, for payloads < 240 bytes) + * **Total Baseline Overhead:** **5 bytes (40 bits)**. + *(Note: Since all preceding fields write exactly 8-bit or 16-bit blocks, the bit position remains byte-aligned when writing payload bytes).* + +2. **`RpcWithReplyMessage` (RPCs returning `UniTask`)** + * Message ID: 2 bytes + * `NetId` (packed uint): 1 byte + * `FunctionIndex` (packed int): 1 byte + * `ReplyId` (packed int): 1 byte + * `Payload` Length Prefix: 1 byte + * **Total Baseline Overhead:** **6 bytes (48 bits)**. + +3. **`RpcReply` (Async RPC Response)** + * Message ID: 2 bytes + * `ReplyId` (packed int): 1 byte + * `Success` (bool): 1 bit + * `Payload` Length Prefix: 1 byte + * **Total Baseline Overhead:** **5 bytes (40 bits)**. + *(Note: The 1-bit `Success` bool causes the writer to insert 7 bits of alignment padding before writing the raw span, making the header exactly 5 bytes).* + +--- + +## 2. Serialization Size Estimation Rules + +To statically calculate or estimate the serialized size of a message or RPC, walk the declared field/parameter types and sum their sizes: + +### Fixed-Size Types +* **`bool`:** 1 bit. +* **`byte` / `sbyte`:** 8 bits (1 byte). +* **`char` / `ushort` / `short`:** 16 bits (2 bytes). +* **`float`:** 32 bits (4 bytes). +* **`double`:** 64 bits (8 bytes). +* **`decimal`:** 128 bits (16 bytes). +* **`Vector2`:** 64 bits (8 bytes) — 2 floats. +* **`Vector3`:** 96 bits (12 bytes) — 3 floats. +* **`Vector4` / `Color`:** 128 bits (16 bytes) — 4 floats. +* **`Color32`:** 32 bits (4 bytes). +* **`Rect` / `Plane`:** 128 bits (16 bytes). +* **`Ray`:** 192 bits (24 bytes). +* **`Matrix4x4`:** 512 bits (64 bytes). +* **`Quaternion`:** **29 bits** (packed using `QuaternionPacker.Default9`: 9 bits per element + 2 index bits, unless packing attributes override it). + +### Packed & Variable-Length Types +* **`uint` / `int` / `ulong` / `long` (SQLite varint):** + * $\le 240$: 1 byte (8 bits) + * $\le 2287$: 2 bytes (16 bits) + * $\le 67823$: 3 bytes (24 bits) + * *Estimation Strategy:* Assume **1 byte** for indexes/small IDs, or **2 bytes** for typical numeric quantities. +* **`NetworkIdentity`:** 1 packed uint (Estimate: 1–2 bytes). +* **`NetworkBehaviour`:** 1 packed uint + 1 byte component index (Estimate: 2–3 bytes). +* **`GameObject`:** 1 packed uint (Estimate: 1–2 bytes). + +### Weaver Packing Attributes +* **`[BitCount(N)]`:** exactly $N$ bits. +* **`[BitCountFromRange(min, max)]`:** exactly $\lfloor\log_2(\text{max} - \text{min})\rfloor + 1$ bits. +* **`[FloatPack(max, bitCount)]`:** exactly $\text{bitCount}$ bits. +* **`[FloatPack(max, precision)]`:** exactly $\lfloor\log_2(2 \times \text{max} / \text{precision})\rfloor + 1$ bits. +* **`[Vector2Pack / Vector3Pack / QuaternionPack]`:** uses specified bit sizes. + +--- + +## 3. Compiled Proposed Rules + +We propose expanding Mirage's Roslyn Analyzers across the following 100-based categories: + +### A. Serialization & Message Size (`MIRAGE1500` - `MIRAGE1599`) + +#### `MIRAGE1501`: Message Size Exceeds Safe MTU Limit (Warning) +* **Description:** Warns if the estimated serialized size of a message or RPC exceeds typical MTU limits (e.g., 1200 bytes). Large packets face high IP-level fragmentation, resulting in latency spikes and packet loss. +* **Trigger Example:** + ```csharp + [NetworkMessage] + public struct LargeTelemetryMessage + { + public int SequenceNumber; + public Matrix4x4[] HistoricalTransforms; // 16 matrices = 1024 bytes! + } + ``` +* **Solution:** Split the message, compress fields using packing attributes, or paginate the collection. + +#### `MIRAGE1502`: Unbounded String or Collection (Warning) +* **Description:** Warns if a string or array field lacks size boundaries inside a `[NetworkMessage]` or RPC, as this allows malicious clients to trigger memory allocations or cause deserialization errors. +* **Solution:** Wrap them in custom serialized structures with size limits. + +#### `MIRAGE1503`: High Bit-Overhead Primitive Type (Info) +* **Description:** Suggests packing floats/vectors in high-frequency messages (e.g. movement, combat state updates). +* **Solution:** Decorate floats/vectors with `[FloatPack]` or `[Vector3Pack]`. + +--- + +### B. SyncVars and SyncObjects (`MIRAGE1000` - `MIRAGE1099`) + +#### `MIRAGE1003`: Direct Mutation of SyncCollection Elements (Warning) +* **Description:** Warns when fields inside a class element stored in a `SyncList` or `SyncDictionary` are modified directly (e.g., `players[index].health -= 10`). Since the collection reference remains identical, the change isn't tracked and won't trigger synchronization. +* **Solution:** Invoke `SetItemDirtyAt(index)` manually, or switch the element type to a struct and re-assign it. + +#### `MIRAGE1004`: Reassignment of SyncObject Fields (Error) +* **Description:** Triggers an error if a field implementing `ISyncObject` (like `SyncList`) is reassigned or not marked `readonly`. If a SyncObject is reassigned, Mirage continues syncing the old instance while runtime code interacts with the new instance. +* **Solution:** Declare SyncObject fields as `readonly`. + +#### `MIRAGE1005`: High SyncVar Count in NetworkBehaviour (Warning) +* **Description:** Warns if a component has too many SyncVars (e.g., >16), which indicates bloat. +* **Solution:** Group variables into a struct or split into multiple components. + +--- + +### C. RPC Performance & Constraints (`MIRAGE1200` - `MIRAGE1299`) + +#### `MIRAGE1202`: Pass-by-Reference Modifiers in RPCs (Error) +* **Description:** Flags parameters in RPCs that use `ref`, `in`, or `out` modifiers, since the serialization weaver cannot process references. +* **Solution:** Pass parameters by value. + +#### `MIRAGE1203`: Static RPC Methods (Error) +* **Description:** Flags static methods decorated with `[ServerRpc]` or `[ClientRpc]`. RPCs require instanced object contexts to route correctly. +* **Solution:** Make the method non-static. + +#### `MIRAGE1204`: Invalid ClientRpc Target Configurations (Error) +* **Description:** Ensures consistency: `target = RpcTarget.Player` must have `INetworkPlayer` as the first parameter, and `target = RpcTarget.Owner` cannot use `excludeOwner = true`. +* **Solution:** Fix parameter signature or exclusion properties. + +#### `MIRAGE1205`: Invalid RateLimit Attribute Settings (Error) +* **Description:** Assures `Interval`, `Refill`, `MaxTokens`, and `Penalty` inside `[RateLimit]` are positive and greater than zero. + +--- + +### D. General API & Lifecycle Constraints (`MIRAGE1400` - `MIRAGE1499`) + +#### `MIRAGE1401`: Unity Lifecycle Method Network Guards (Warning) +* **Description:** Warns if a network guard (e.g., `[Server]`) is placed on Unity lifecycle methods (`Update`, `FixedUpdate`) without setting `error = false`. Since Unity calls these on all clients automatically, the default throwing behavior will crash client consoles. +* **Solution:** Set `[Server(error = false)]` or use an explicit check (`if (!IsServer) return`). + +#### `MIRAGE1402`: Accessing Network State in Awake/Start (Warning) +* **Description:** Flags checks on network state properties (`IsServer`, `IsClient`) or RPC calls inside `Awake()` or `Start()`. The object is not spawned yet, so these evaluate to `false` and RPCs fail. +* **Solution:** Register listeners to `Identity.OnStartServer` or `Identity.OnStartClient` instead. + +#### `MIRAGE1403`: Missing base Call in OnSerialize/OnDeserialize Overrides (Error) +* **Description:** Overriding `OnSerialize` or `OnDeserialize` in a component containing SyncVars/SyncObjects without calling `base.OnSerialize/OnDeserialize` stops Weaver-generated syncing from running. +* **Solution:** Add base class serialization calls. diff --git a/CSharpAnalyzers/analyzers_roadmap.md b/CSharpAnalyzers/analyzers_roadmap.md index 85a545d5a6a..2c693c2059f 100644 --- a/CSharpAnalyzers/analyzers_roadmap.md +++ b/CSharpAnalyzers/analyzers_roadmap.md @@ -2,60 +2,66 @@ This document tracks the implementation status of the proposed Roslyn Analyzers for Mirage, updated with user feedback to avoid gaps and adjust severities/rules. +note on format: 3 check boxes [ ][ ][ ] +- researched and added correct details to roadmap +- docs/sample page +- added unit tests for CSharpAnalyzers + +IMPORTANT: if boxes arn't checked here, assume existing files are out-of-date and need reviewing as if they do not exist --- ## Group 1: SyncVars & SyncObjects (`MIRAGE1000 – MIRAGE1099`) -* [ ] **`MIRAGE1001`**: Warning: SyncVar Class Warning (Warns on class-based SyncVars / SyncObject type arguments, including types like List, T[], Dictionary). -* [ ] **`MIRAGE1002`**: Error: SyncVar Auto-Property Error (Requires SyncVar properties to be non-static auto-properties with both get/set). -* [ ] **`MIRAGE1003`**: Warning: Direct Mutation of SyncCollection Elements (Warning when modifying fields of class elements inside a `SyncList`/`SyncDictionary` without calling `SetItemDirty`). (ignore sync-by-reference like NetworkIdentity/Behaviour) -* [ ] **`MIRAGE1004`**: Error: Reassignment of SyncObject Fields, error when not assigned and not readonly (Error when `ISyncObject` fields like `SyncList` are reassigned or not marked `readonly`). -* [ ] **`MIRAGE1005`**: Error: Invalid SyncVar Hook Method (Error when a SyncVar hook method does not exist in the class, is static, or does not match the signature `void Hook(T oldValue, T newValue)`). **IMPORTANT TODO: check real syncvar hook rules from weaver code, they can be methods or events** +* [x][x][x] **`MIRAGE1001`**: Warning: SyncVar Class Warning (Warns on class-based SyncVars / SyncObject type arguments, including types like List, T[], Dictionary). +* [x][x][x] **`MIRAGE1002`**: Error: SyncVar Auto-Property Error (Requires SyncVar properties to be non-static auto-properties with both get/set). +* [x][x][x] **`MIRAGE1003`**: Warning: Direct Mutation of SyncCollection Elements (Warning when modifying fields of class elements inside a `SyncList`/`SyncDictionary` without calling `SetItemDirty`). (ignore sync-by-reference like NetworkIdentity/Behaviour) +* [x][x][x] **`MIRAGE1004`**: Error: Reassignment of SyncObject Fields, error when not assigned and not readonly (Error when `ISyncObject` fields like `SyncList` are reassigned or not marked `readonly`). +* [x][x][ ] **`MIRAGE1005`**: Error: Invalid SyncVar Hook Method (Error when a SyncVar hook method does not exist in the class, or does not match the signature `void Hook()`, `void Hook(T newValue)`, or `void Hook(T oldValue, T newValue)`. For events, it can be an instance event of type `Action`, `Action`, or `Action`, but it must not be static). Methods can be static, Events can't be static. --- ## Group 2: Network Behaviour & Attribute Placement (`MIRAGE1100 – MIRAGE1199`) -* [ ] **`MIRAGE1101`**: Error: Misplaced Network Attribute Error (Verifies network attributes are only on classes inheriting from `NetworkBehaviour`). -* [ ] **`MIRAGE1102`**: Warning: Redundant Server/Client Attribute on RPC (Warning if a method has both `[ServerRpc]` and `[Server]` or `[ClientRpc]` and `[Client]`). +* [x][x][x] **`MIRAGE1101`**: Error: Misplaced Network Attribute Error (Verifies network attributes are only on classes inheriting from `NetworkBehaviour`). +* [x][x][ ] **`MIRAGE1102`**: Warning: Redundant Server/Client Attribute on RPC (Warning if a method has both `[ServerRpc]` and `[Server]` or `[ClientRpc]` and `[Client]`). --- ## Group 3: Remote Procedure Calls (`MIRAGE1200 – MIRAGE1299`) -* [ ] **`MIRAGE1201`**: Warning: NetworkMessage/RPC Class Warning (Warns on class-based fields/parameters, ignoring types like List, T[], Dictionary). -* [ ] **`MIRAGE1202`**: Error: RPC Signature Error, generic allowed, void allowed, `UniTask` (with return value) allowed, `UniTask` (not return value) not allowed. -* [ ] **`MIRAGE1203`**: Error: Pass-by-Reference Modifiers in RPCs (Error if parameters use `ref`, `in`, or `out`). -* [ ] **`MIRAGE1204`**: Error: Static RPC Methods (Error if an RPC method is marked `static`). -* [ ] **`MIRAGE1205`**: Error: Invalid ClientRpc Target Configurations, if using RpcTarget.Player, first Argument should be INetworkPlayer. -* [ ] **`MIRAGE1206`**: Error: Invalid RateLimit Attribute Settings (Error on zero/negative values inside `[RateLimit]`). -* [ ] **`MIRAGE1207`**: Warning: Missing RateLimit on ServerRpc (Warning if a `[ServerRpc]` method is missing the `[RateLimit]` attribute, as all client->server messages should be limited). +* [x][x][x] **`MIRAGE1201`**: Warning: NetworkMessage/RPC Class Warning (Warns on class-based fields/parameters, ignoring types like List, T[], Dictionary). +* [x][x][x] **`MIRAGE1202`**: Error: RPC Signature Error, generic allowed, void allowed, `UniTask` (with return value) allowed, `UniTask` (not return value) not allowed. +* [x][x][x] **`MIRAGE1203`**: Error: Pass-by-Reference Modifiers in RPCs (Error if parameters use `ref`, `in`, or `out`). +* [x][x][x] **`MIRAGE1204`**: Error: Static RPC Methods (Error if an RPC method is marked `static`). +* [x][x][x] **`MIRAGE1205`**: Error: Invalid ClientRpc Target Configurations, if using RpcTarget.Player, first Argument should be INetworkPlayer. +* [x][x][x] **`MIRAGE1206`**: Error: Invalid RateLimit Attribute Settings (Error on zero/negative values inside `[RateLimit]`). +* [x][x][x] **`MIRAGE1207`**: Warning: Missing RateLimit on ServerRpc (Warning if a `[ServerRpc]` method is missing the `[RateLimit]` attribute, as all client->server messages should be limited). --- ## Group 4: Serialization (`MIRAGE1300 – MIRAGE1399`) -* [ ] **`MIRAGE1301`**: Error: Field Type Serialization Validation (Error if type inside `[NetworkMessage]` or RPC is not serializable and has no custom serializer). -* [ ] **`MIRAGE1302`**: (SKIP: this will be added later when we have source gen instead of Weaver dll edit) Warning: Field Type Serialization Validation, private fields or properties that will not be serialized (eg, structs sent over the network should be clean and minimal to avoid developer confusion). -* [ ] **`MIRAGE1303`**: Error: Mismatched Custom Serialization Methods (Error if custom writer `Write` exists without matching reader `Read` or vice-versa - implemented as Error to prevent Weaver compile errors). -* [ ] **`MIRAGE1304`**: Error: Non-Serializable MonoBehaviour Parameter (Error if RPC parameter or NetworkMessage field is a `MonoBehaviour` type that does not inherit from `NetworkBehaviour`). -* [ ] **`MIRAGE1305`**: Warning: Missing `[NetworkMessage]` Attribute (Warning if a type is sent or registered as a message, but is missing the `[NetworkMessage]` attribute). +* [x][x][x] **`MIRAGE1301`**: Error: Field Type Serialization Validation (Error if type inside `[NetworkMessage]` or RPC is not serializable and has no custom serializer). +* [x][x][ ] **`MIRAGE1302`**: (SKIP: this will be added later when we have source gen instead of Weaver dll edit) Warning: Field Type Serialization Validation, private fields or properties that will not be serialized (eg, structs sent over the network should be clean and minimal to avoid developer confusion). +* [x][x][x] **`MIRAGE1303`**: Error: Mismatched Custom Serialization Methods (Error if custom writer `Write` exists without matching reader `Read` or vice-versa - implemented as Error to prevent Weaver compile errors). +* [x][x][ ] **`MIRAGE1304`**: Error: Non-Serializable MonoBehaviour Parameter (Error if RPC parameter or NetworkMessage field is a `MonoBehaviour` type that does not inherit from `NetworkBehaviour`). +* [x][x][ ] **`MIRAGE1305`**: Warning: Missing `[NetworkMessage]` Attribute (Warning if a type is sent or registered as a message, but is missing the `[NetworkMessage]` attribute). --- ## Group 5: General API & Lifecycles (`MIRAGE1400 – MIRAGE1499`) -* [ ] **`MIRAGE1401`**: Accessing Network State in Awake/Start (Warning if checking `IsServer`/`IsClient`, accessing network behavior/identity properties, or calling RPCs inside `Awake()` or `Start()`). -* [ ] **`MIRAGE1402`**: Missing base Call in OnSerialize/OnDeserialize (Warning if overrides do not call base implementations in components containing SyncVars or SyncObjects). +* [x][x][x] **`MIRAGE1401`**: Accessing Network State in Awake/Start (Warning if checking `IsServer`/`IsClient`, accessing network behavior/identity properties, or calling RPCs inside `Awake()` or `Start()`). +* [x][x][x] **`MIRAGE1402`**: Missing base Call in OnSerialize/OnDeserialize (Warning if overrides do not call base implementations in components containing SyncVars or SyncObjects). --- ## Group 6: Network Performance & Size Estimation (`MIRAGE1500 – MIRAGE1599`) *Note: These rules are designated as `DiagnosticSeverity.Warning` to warn about security/performance issues, and output a parser-friendly format (e.g. JSON-like summary of size calculations) to facilitate future CodeLens or editor tooling integration.* -* [ ] **`MIRAGE1501`**: Network Message Exceeds Safe MTU (Warning if estimated serialization size exceeds 1200 bytes). -* [ ] **`MIRAGE1502`**: Unbounded String or Collection (Warning if string/collection has no defined size bounds). -* [ ] **`MIRAGE1503`**: High Bit-Over-Head Primitive Type (Warning on uncompressed float/vector transfers). +* [x][x][x] **`MIRAGE1501`**: Network Message Exceeds Safe MTU (Warning if estimated serialization size exceeds 1200 bytes). +* [x][x][x] **`MIRAGE1502`**: Unbounded String or Collection (Warning if string/collection has no defined size bounds). +* [x][x][x] **`MIRAGE1503`**: High Bit-Over-Head Primitive Type (Warning on uncompressed float/vector transfers). --- diff --git a/doc/docs/analyzers/MIRAGE1005.md b/doc/docs/analyzers/MIRAGE1005.md new file mode 100644 index 00000000000..770e7837ae0 --- /dev/null +++ b/doc/docs/analyzers/MIRAGE1005.md @@ -0,0 +1,28 @@ +# MIRAGE1005: Invalid SyncVar Hook Method + +## The Problem +A property marked with `[SyncVar]` specifies a hook name in its attribute, but the hook cannot be resolved, is static (for events), is ambiguous, or does not match the required signature. + +Mirage's IL Weaver post-processes compiled assemblies by intercepting property writes to call user-defined hook methods/events. For this injection to succeed, the hook must be a valid method or event in the declaring class and must match the expected signatures. If the Weaver cannot resolve the hook, it will throw a compilation error, halting the build. + +Specifically, the rules for SyncVar hooks are: +1. **Resolution:** The method or event must exist in the class. +2. **Signature Matching:** Parameters of the hook method or event must match the type of the SyncVar exactly. + - For Methods: `0`, `1`, or `2` parameters are allowed (e.g., `void Hook()`, `void Hook(T newValue)`, or `void Hook(T oldValue, T newValue)`). + - For Events: Must be a `System.Action` (generic or non-generic) with `0`, `1`, or `2` parameters. +3. **Ambiguity:** Under the default `SyncHookType.Automatic` mode, if multiple overloads (e.g., a method and an event, or methods with different parameter counts) exist with the same name, the Weaver cannot automatically determine which to call and will throw an error. Use explicit `hookType` or rename/remove overloads to resolve. +4. **Static Constraints:** + - **Methods:** Can be `static` (supported by Weaver). + - **Events:** Cannot be `static` (Weaver requires instance events and will fail to compile). + +--- + +## Example of Triggering Code +{{{ Path:'Snippets/Analyzers/Mirage1005.cs' Name:'mirage1005-triggering' }}} + +--- + +## How to Resolve +Verify the spelling of the hook name, ensure all parameters match the SyncVar's type exactly, avoid static events, and explicitly define the `hookType` if overload resolution is ambiguous. + +{{{ Path:'Snippets/Analyzers/Mirage1005.cs' Name:'mirage1005-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1101.md b/doc/docs/analyzers/MIRAGE1101.md index 17225980352..9a9c237166d 100644 --- a/doc/docs/analyzers/MIRAGE1101.md +++ b/doc/docs/analyzers/MIRAGE1101.md @@ -3,16 +3,12 @@ ## The Problem A Mirage-specific attribute (such as `[SyncVar]`, `[Server]`, `[Client]`, `[ServerRpc]`, `[ClientRpc]`, `[HasAuthority]`, `[LocalPlayer]`, or `[NetworkMethod]`) is declared on a field, property, or method inside a class that does not inherit from `NetworkBehaviour`. -These attributes control network synchronization or inject runtime active checks (guards) that require the state and context of a `NetworkBehaviour` instance. Using them in standard `MonoBehaviour` or plain C# classes is invalid and will cause compile or weaver failures. - ---- +These attributes control network synchronization or inject runtime active checks (guards) that require the state and context of a `NetworkBehaviour` instance. Using them in standard `MonoBehaviour` or plain C# classes is invalid and will cause compile or Weaver failures. ## Example of Triggering Code {{{ Path:'Snippets/Analyzers/Mirage1101.cs' Name:'mirage1101-triggering' }}} ---- - ## How to Resolve - Ensure that the declaring class inherits from `NetworkBehaviour` instead of `MonoBehaviour` or other base classes. + {{{ Path:'Snippets/Analyzers/Mirage1101.cs' Name:'mirage1101-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1102.md b/doc/docs/analyzers/MIRAGE1102.md new file mode 100644 index 00000000000..d669e7cd090 --- /dev/null +++ b/doc/docs/analyzers/MIRAGE1102.md @@ -0,0 +1,17 @@ +# MIRAGE1102: Redundant Server/Client Attribute on RPC + +## The Problem +An RPC method is decorated with both a routing attribute (`[ServerRpc]` or `[ClientRpc]`) and its corresponding active guard attribute (`[Server]` or `[Client]`). + +- Declaring `[Server]` on a method marked with `[ServerRpc]` is redundant because a ServerRpc can only execute on the server. +- Declaring `[Client]` on a method marked with `[ClientRpc]` is redundant because a ClientRpc can only execute on clients. + +Adding both attributes triggers unnecessary active guard injection during weaving, increases code clutter, and can cause confusion about the method's lifecycle. + +## Example of Triggering Code +{{{ Path:'Snippets/Analyzers/Mirage1102.cs' Name:'mirage1102-triggering' }}} + +## How to Resolve +Remove the redundant active guard attribute (`[Server]` or `[Client]`) from the RPC method. + +{{{ Path:'Snippets/Analyzers/Mirage1102.cs' Name:'mirage1102-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1201.md b/doc/docs/analyzers/MIRAGE1201.md index 9d588dac66a..0cb0995685e 100644 --- a/doc/docs/analyzers/MIRAGE1201.md +++ b/doc/docs/analyzers/MIRAGE1201.md @@ -1,11 +1,13 @@ -# MIRAGE1201: RPC Signature Error +# MIRAGE1201: NetworkMessage/RPC Class Warning ## The Problem -A method decorated with `[ServerRpc]` or `[ClientRpc]` violates remote procedure call rules: -1. **Generic Methods:** The method cannot have generic parameters (e.g. `void MyRpc()`). -2. **Invalid Return Type:** The method must return `void`, `UniTask`, or `UniTask`. Returning standard tasks, custom classes, or primitive values is invalid. +A field or property in a class/struct marked with `[NetworkMessage]`, or a parameter/return type argument in a method marked with `[ServerRpc]` or `[ClientRpc]`, uses a class type instead of a value type/struct. -Remote procedure calls must serialize their arguments and return values across the network. Non-generic signatures and async return wrappers (`UniTask`) are required for the Mirage Weaver to generate remote execution logic correctly. +Class-based types are generally risky for network messaging because: +1. **Allocations:** Mirage must allocate a new object instance upon deserialization, which causes garbage collection (GC) spikes and performance overhead. +2. **Polymorphism Limitations:** Mirage's standard serialization only serializes fields of the declared member type, not the concrete derived subclass type. + +*Note: Standard collections (such as `List` or `Dictionary` in `System.Collections.Generic`) are automatically ignored by this rule as they are natively supported by Mirage's Weaver and serialization system.* --- @@ -16,7 +18,14 @@ Remote procedure calls must serialize their arguments and return values across t ## How to Resolve -1. Make the method non-generic. -2. Ensure the return type is `void` or a valid async task wrapper like `UniTask` (or `UniTask` for asynchronous RPCs). +### Option 1: Use a struct (Recommended) +Structs (value types) avoid memory allocations and guarantee safe copy-by-value semantics. +{{{ Path:'Snippets/Analyzers/Mirage1201.cs' Name:'mirage1201-struct-option' }}} + +### Option 2: Implement Custom Serialization and mark the class as safe +If you want to use the class type and manage performance/reference safety yourself, write custom `Write` and `Read` extension methods for the class, and decorate the class with `[WeaverSafeClass]` to suppress the warning globally. +{{{ Path:'Snippets/Analyzers/Mirage1201.cs' Name:'mirage1201-class-option' }}} -{{{ Path:'Snippets/Analyzers/Mirage1201.cs' Name:'mirage1201-resolved' }}} +### Option 3: Suppress the warning on the member or parameter +If you want to disable the warning only on a specific field, property, or parameter, decorate it with `[WeaverSafeClass]`. +{{{ Path:'Snippets/Analyzers/Mirage1201.cs' Name:'mirage1201-suppress-option' }}} diff --git a/doc/docs/analyzers/MIRAGE1202.md b/doc/docs/analyzers/MIRAGE1202.md index 2162409e1d7..11512b3de7f 100644 --- a/doc/docs/analyzers/MIRAGE1202.md +++ b/doc/docs/analyzers/MIRAGE1202.md @@ -1,9 +1,11 @@ -# MIRAGE1202: Pass-by-Reference Modifiers in RPCs +# MIRAGE1202: RPC Signature Error ## The Problem -An RPC method contains parameters with `ref` or `out` parameter modifiers. +A method decorated with `[ServerRpc]` or `[ClientRpc]` violates remote procedure call rules: +1. **Generic Methods:** The method cannot have generic parameters (e.g. `void MyRpc()`). +2. **Invalid Return Type:** The method must return `void`, `UniTask`, or `UniTask`. Returning standard tasks, custom classes, or primitive values is invalid. -RPCs (Remote Procedure Calls) serialize arguments and send them over the network. Pass-by-reference modifiers (`ref` or `out`) imply that the method can modify the argument and pass the changes back to the caller in-place, which is impossible over a one-way network serialization boundary. +Remote procedure calls must serialize their arguments and return values across the network. Non-generic signatures and async return wrappers (`UniTask`) are required for the Mirage Weaver to generate remote execution logic correctly. --- @@ -14,6 +16,7 @@ RPCs (Remote Procedure Calls) serialize arguments and send them over the network ## How to Resolve -Pass parameters by value. If you need to communicate updated state back to the caller, either use an asynchronous RPC with a `UniTask` return value or update a synchronized property (such as a `[SyncVar]`). +1. Make the method non-generic. +2. Ensure the return type is `void` or a valid async task wrapper like `UniTask` (or `UniTask` for asynchronous RPCs). {{{ Path:'Snippets/Analyzers/Mirage1202.cs' Name:'mirage1202-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1203.md b/doc/docs/analyzers/MIRAGE1203.md index 9c9516431fe..b23b13102d2 100644 --- a/doc/docs/analyzers/MIRAGE1203.md +++ b/doc/docs/analyzers/MIRAGE1203.md @@ -1,9 +1,9 @@ -# MIRAGE1203: Static RPC Methods +# MIRAGE1203: Pass-by-Reference Modifiers in RPCs ## The Problem -An RPC method decorated with `[ServerRpc]` or `[ClientRpc]` is declared as `static`. +An RPC method contains parameters with `ref` or `out` parameter modifiers. -RPC methods must execute on a specific instance of a `NetworkBehaviour` on a specific `GameObject` so that Mirage knows which network identity the message is targeted at. Static methods lack an instance context (`this`), making it impossible to route the message to the correct network object. +RPCs (Remote Procedure Calls) serialize arguments and send them over the network. Pass-by-reference modifiers (`ref` or `out`) imply that the method can modify the argument and pass the changes back to the caller in-place, which is impossible over a one-way network serialization boundary. --- @@ -14,6 +14,6 @@ RPC methods must execute on a specific instance of a `NetworkBehaviour` on a spe ## How to Resolve -Remove the `static` modifier from the RPC method declaration so it runs within the instance context of a spawned `NetworkBehaviour`. +Pass parameters by value. If you need to communicate updated state back to the caller, either use an asynchronous RPC with a `UniTask` return value or update a synchronized property (such as a `[SyncVar]`). {{{ Path:'Snippets/Analyzers/Mirage1203.cs' Name:'mirage1203-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1204.md b/doc/docs/analyzers/MIRAGE1204.md index eeb6b51e9ac..56ad8c06bfa 100644 --- a/doc/docs/analyzers/MIRAGE1204.md +++ b/doc/docs/analyzers/MIRAGE1204.md @@ -1,11 +1,9 @@ -# MIRAGE1204: Invalid ClientRpc Target Configurations +# MIRAGE1204: Static RPC Methods ## The Problem -A `[ClientRpc]` target configuration is invalid for one of the following reasons: -1. The method return type is `UniTask` or `UniTask` (it returns values) but the target is configured as `RpcTarget.Observers`. -2. The target is set to `RpcTarget.Player` but the first parameter of the method is not an `INetworkPlayer` (or `NetworkConnection`) to specify the recipient. +An RPC method decorated with `[ServerRpc]` or `[ClientRpc]` is declared as `static`. -Broadcast RPCs (where the target is `Observers`) cannot collect return values since multiple clients would respond. Returning values requires a single, specific destination (e.g. `RpcTarget.Owner` or `RpcTarget.Player`). Furthermore, when targeting a specific `Player`, Mirage needs to know which connection to send the RPC to, so the method's first parameter must be the player connection. +RPC methods must execute on a specific instance of a `NetworkBehaviour` on a specific `GameObject` so that Mirage knows which network identity the message is targeted at. Static methods lack an instance context (`this`), making it impossible to route the message to the correct network object. --- @@ -16,7 +14,6 @@ Broadcast RPCs (where the target is `Observers`) cannot collect return values si ## How to Resolve -1. If the RPC returns values, change the target to `RpcTarget.Owner` or `RpcTarget.Player`. -2. If the RPC targets `RpcTarget.Player`, ensure the first parameter is of type `INetworkPlayer` (or `NetworkConnection`). +Remove the `static` modifier from the RPC method declaration so it runs within the instance context of a spawned `NetworkBehaviour`. {{{ Path:'Snippets/Analyzers/Mirage1204.cs' Name:'mirage1204-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1205.md b/doc/docs/analyzers/MIRAGE1205.md index e622cda79f6..c8fe815f1c4 100644 --- a/doc/docs/analyzers/MIRAGE1205.md +++ b/doc/docs/analyzers/MIRAGE1205.md @@ -1,12 +1,11 @@ -# MIRAGE1205: Invalid RateLimit Attribute Settings +# MIRAGE1205: Invalid ClientRpc Target Configurations ## The Problem -The `[RateLimit]` attribute contains invalid configurations. This includes: -1. `Interval` is less than or equal to zero. -2. `Refill` is less than or equal to zero. -3. `MaxTokens` is less than or equal to zero, or is less than the `Refill` rate. +A `[ClientRpc]` target configuration is invalid for one of the following reasons: +1. The method return type is `UniTask` or `UniTask` (it returns values) but the target is configured as `RpcTarget.Observers`. +2. The target is set to `RpcTarget.Player` but the first parameter of the method is not an `INetworkPlayer` (or `NetworkConnection`) to specify the recipient. -Rate limiting buckets require positive numbers for intervals, refill rates, and max tokens to correctly configure token replenishment cycles. If any of these values are zero or negative, or if `MaxTokens` is set to a value less than `Refill`, the rate limiting logic will fail to function or cause infinite loops/resource starvation on the server. +Broadcast RPCs (where the target is `Observers`) cannot collect return values since multiple clients would respond. Returning values requires a single, specific destination (e.g. `RpcTarget.Owner` or `RpcTarget.Player`). Furthermore, when targeting a specific `Player`, Mirage needs to know which connection to send the RPC to, so the method's first parameter must be the player connection. --- @@ -17,6 +16,7 @@ Rate limiting buckets require positive numbers for intervals, refill rates, and ## How to Resolve -Correct the parameters of the `[RateLimit]` attribute to ensure they are positive, valid values. Ensure `MaxTokens` is at least equal to the `Refill` value. +1. If the RPC returns values, change the target to `RpcTarget.Owner` or `RpcTarget.Player`. +2. If the RPC targets `RpcTarget.Player`, ensure the first parameter is of type `INetworkPlayer` (or `NetworkConnection`). {{{ Path:'Snippets/Analyzers/Mirage1205.cs' Name:'mirage1205-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1206.md b/doc/docs/analyzers/MIRAGE1206.md index 15f670543f5..ad1ebd78ca6 100644 --- a/doc/docs/analyzers/MIRAGE1206.md +++ b/doc/docs/analyzers/MIRAGE1206.md @@ -1,9 +1,12 @@ -# MIRAGE1206: Missing RateLimit on ServerRpc +# MIRAGE1206: Invalid RateLimit Attribute Settings ## The Problem -A `[ServerRpc]` method is declared without a `[RateLimit]` attribute. +The `[RateLimit]` attribute contains invalid configurations. This includes: +1. `Interval` is less than or equal to zero. +2. `Refill` is less than or equal to zero. +3. `MaxTokens` is less than or equal to zero, or is less than the `Refill` rate. -To prevent denial of service (DoS) attacks, server CPU strain, and memory bloat from client RPC spam, it is highly recommended to apply a `[RateLimit]` attribute to every `[ServerRpc]` method. Without a rate limit, a malicious client could flood the server with requests, leading to server performance degradation or player disconnects. +Rate limiting buckets require positive numbers for intervals, refill rates, and max tokens to correctly configure token replenishment cycles. If any of these values are zero or negative, or if `MaxTokens` is set to a value less than `Refill`, the rate limiting logic will fail to function or cause infinite loops/resource starvation on the server. --- @@ -14,6 +17,6 @@ To prevent denial of service (DoS) attacks, server CPU strain, and memory bloat ## How to Resolve -Add a `[RateLimit]` attribute to the `[ServerRpc]` method with appropriate parameters for the expected rate of call. +Correct the parameters of the `[RateLimit]` attribute to ensure they are positive, valid values. Ensure `MaxTokens` is at least equal to the `Refill` value. {{{ Path:'Snippets/Analyzers/Mirage1206.cs' Name:'mirage1206-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1207.md b/doc/docs/analyzers/MIRAGE1207.md new file mode 100644 index 00000000000..e4826649cce --- /dev/null +++ b/doc/docs/analyzers/MIRAGE1207.md @@ -0,0 +1,19 @@ +# MIRAGE1207: Missing RateLimit on ServerRpc + +## The Problem +A `[ServerRpc]` method is declared without a `[RateLimit]` attribute. + +To prevent denial of service (DoS) attacks, server CPU strain, and memory bloat from client RPC spam, it is highly recommended to apply a `[RateLimit]` attribute to every `[ServerRpc]` method. Without a rate limit, a malicious client could flood the server with requests, leading to server performance degradation or player disconnects. + +--- + +## Example of Triggering Code +{{{ Path:'Snippets/Analyzers/Mirage1207.cs' Name:'mirage1207-triggering' }}} + +--- + +## How to Resolve + +Add a `[RateLimit]` attribute to the `[ServerRpc]` method with appropriate parameters for the expected rate of call. + +{{{ Path:'Snippets/Analyzers/Mirage1207.cs' Name:'mirage1207-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1301.md b/doc/docs/analyzers/MIRAGE1301.md index 8e8c514f332..6bb1e821e03 100644 --- a/doc/docs/analyzers/MIRAGE1301.md +++ b/doc/docs/analyzers/MIRAGE1301.md @@ -1,13 +1,9 @@ -# MIRAGE1301: Message or RPC Class Warning +# MIRAGE1301: Field Type Serialization Validation ## The Problem -A field or property in a class/struct marked with `[NetworkMessage]`, or a parameter/return type argument in a method marked with `[ServerRpc]` or `[ClientRpc]`, uses a class type instead of a value type/struct. +A field or property in a class/struct marked with `[NetworkMessage]`, or a parameter in a method marked with `[ServerRpc]` or `[ClientRpc]`, uses a type that Mirage does not know how to serialize, and no custom writer/reader has been registered or generated for it. -Class-based types are generally risky for network messaging because: -1. **Allocations:** Mirage must allocate a new object instance upon deserialization, which causes garbage collection (GC) spikes and performance overhead. -2. **Polymorphism Limitations:** Mirage's standard serialization only serializes fields of the declared member type, not the concrete derived subclass type. - -*Note: Standard collections (such as `List` or `Dictionary` in `System.Collections.Generic`) are automatically ignored by this rule as they are natively supported by Mirage's Weaver and serialization system.* +Mirage uses compile-time IL weaving to generate serialization code for NetworkMessages and RPCs. If a field or parameter type is not a primitive type, an existing supported type, or a type that can be auto-weaved (like a simple struct/class with only serializable fields), and there are no custom `NetworkWriter` or `NetworkReader` extension methods for it, the Weaver will fail because it cannot serialize the data. --- @@ -18,14 +14,6 @@ Class-based types are generally risky for network messaging because: ## How to Resolve -### Option 1: Use a struct (Recommended) -Structs (value types) avoid memory allocations and guarantee safe copy-by-value semantics. -{{{ Path:'Snippets/Analyzers/Mirage1301.cs' Name:'mirage1301-struct-option' }}} - -### Option 2: Implement Custom Serialization and mark the class as safe -If you want to use the class type and manage performance/reference safety yourself, write custom `Write` and `Read` extension methods for the class, and decorate the class with `[WeaverSafeClass]` to suppress the warning globally. -{{{ Path:'Snippets/Analyzers/Mirage1301.cs' Name:'mirage1301-class-option' }}} +Ensure all fields are of serializable types. If you need to send a custom type, make sure it is a struct/class with only serializable fields, or implement custom `Write` and `Read` extension methods for the custom type so that Mirage knows how to serialize it. -### Option 3: Suppress the warning on the member or parameter -If you want to disable the warning only on a specific field, property, or parameter, decorate it with `[WeaverSafeClass]`. -{{{ Path:'Snippets/Analyzers/Mirage1301.cs' Name:'mirage1301-suppress-option' }}} +{{{ Path:'Snippets/Analyzers/Mirage1301.cs' Name:'mirage1301-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1302.md b/doc/docs/analyzers/MIRAGE1302.md index e144bb33641..175ac2e3353 100644 --- a/doc/docs/analyzers/MIRAGE1302.md +++ b/doc/docs/analyzers/MIRAGE1302.md @@ -1,9 +1,11 @@ -# MIRAGE1302: Field Type Serialization Validation +# MIRAGE1302: Unserialized Private Field Warning ## The Problem -A field or property in a class/struct marked with `[NetworkMessage]`, or a parameter in a method marked with `[ServerRpc]` or `[ClientRpc]`, uses a type that Mirage does not know how to serialize, and no custom writer/reader has been registered or generated for it. +A private field or property is declared inside a `[NetworkMessage]` struct or class. -Mirage uses compile-time IL weaving to generate serialization code for NetworkMessages and RPCs. If a field or parameter type is not a primitive type, an existing supported type, or a type that can be auto-weaved (like a simple struct/class with only serializable fields), and there are no custom `NetworkWriter` or `NetworkReader` extension methods for it, the Weaver will fail because it cannot serialize the data. +In Mirage, private fields and properties are ignored by the Weaver during automatic serialization. Only public fields are serialized and sent over the network. If a developer declares private fields expecting them to be networked, it can lead to confusion and logic bugs because they will remain uninitialized or hold default values on the receiving end. + +*Note: This is a placeholder warning designated for future release (skipped for now until source generation is fully integrated).* --- @@ -14,6 +16,6 @@ Mirage uses compile-time IL weaving to generate serialization code for NetworkMe ## How to Resolve -Ensure all fields are of serializable types. If you need to send a custom type, make sure it is a struct/class with only serializable fields, or implement custom `Write` and `Read` extension methods for the custom type so that Mirage knows how to serialize it. +Make the field public so it is automatically picked up by the Weaver for serialization. If the field is intended to be purely local and not serialized, you can ignore this warning, or mark it as static if applicable. {{{ Path:'Snippets/Analyzers/Mirage1302.cs' Name:'mirage1302-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1304.md b/doc/docs/analyzers/MIRAGE1304.md new file mode 100644 index 00000000000..257e9af9551 --- /dev/null +++ b/doc/docs/analyzers/MIRAGE1304.md @@ -0,0 +1,21 @@ +# MIRAGE1304: Non-Serializable MonoBehaviour Parameter + +## The Problem +An RPC parameter or a `[NetworkMessage]` field is a `MonoBehaviour` type (or a subclass of it) that does not inherit from `NetworkBehaviour`. + +In Unity, a basic `MonoBehaviour` represents a local component attached to a GameObject. Because it lacks a network identity (`NetworkIdentity`), Mirage cannot identify which instance of the component to refer to across the network. Consequently, the Weaver cannot automatically generate serialization code for `MonoBehaviour` references, resulting in a compile-time weaving error. + +--- + +## Example of Triggering Code +{{{ Path:'Snippets/Analyzers/Mirage1304.cs' Name:'mirage1304-triggering' }}} + +--- + +## How to Resolve + +To resolve this error, ensure the component type inherits from `NetworkBehaviour` instead of `MonoBehaviour`. Mirage is able to serialize components that inherit from `NetworkBehaviour` by writing their parent `NetworkIdentity` and the component's index. + +Alternatively, if the component itself is purely local and cannot be networked, pass a serializable identifier (like a unique ID, parent `NetworkIdentity`, or string) instead of the component object itself. + +{{{ Path:'Snippets/Analyzers/Mirage1304.cs' Name:'mirage1304-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1305.md b/doc/docs/analyzers/MIRAGE1305.md new file mode 100644 index 00000000000..c3f7f556d99 --- /dev/null +++ b/doc/docs/analyzers/MIRAGE1305.md @@ -0,0 +1,19 @@ +# MIRAGE1305: Missing NetworkMessage Attribute + +## The Problem +A class or struct is used in message-sending methods (like `Send()`) or registered in a message-handling system (like `RegisterHandler()`), but is missing the `[NetworkMessage]` attribute. + +Mirage's post-compilation Weaver scans types decorated with `[NetworkMessage]` to generate serialization helper code and register unique message type IDs (hashes of the full type name). If a type is sent or registered as a handler without the `[NetworkMessage]` attribute, the Weaver will not have generated the necessary metadata or registration wrappers. This results in runtime errors such as failing to serialize, failing to unpack, or "Unexpected message ID" warnings when the message is received. + +--- + +## Example of Triggering Code +{{{ Path:'Snippets/Analyzers/Mirage1305.cs' Name:'mirage1305-triggering' }}} + +--- + +## How to Resolve + +Decorate the target message class or struct with the `[NetworkMessage]` attribute. This instructs Mirage's Weaver to correctly weave code for message serialization, generation, and handler registration. + +{{{ Path:'Snippets/Analyzers/Mirage1305.cs' Name:'mirage1305-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1401.md b/doc/docs/analyzers/MIRAGE1401.md index 6de3c256c60..8311512410a 100644 --- a/doc/docs/analyzers/MIRAGE1401.md +++ b/doc/docs/analyzers/MIRAGE1401.md @@ -20,6 +20,7 @@ Unity's `Awake` and `Start` methods are called during GameObject initialization ## How to Resolve -Override `OnStartServer`, `OnStartClient`, `OnStartLocalPlayer`, or `OnStartAuthority` to run network initialization code when the network state is fully ready. +Subscribe to lifecycle events on `Identity` (such as `Identity.OnStartServer`, `Identity.OnStartClient`, `Identity.OnStartLocalPlayer`, or `Identity.OnAuthorityChanged`) during `Awake()` to execute your network initialization code when the network state is fully ready. {{{ Path:'Snippets/Analyzers/Mirage1401.cs' Name:'mirage1401-resolved' }}} + diff --git a/doc/docs/analyzers/index.md b/doc/docs/analyzers/index.md index 4a5b1d5db3f..f3c3bef721b 100644 --- a/doc/docs/analyzers/index.md +++ b/doc/docs/analyzers/index.md @@ -12,16 +12,21 @@ Mirage uses Roslyn Analyzers to provide compile-time validation for network code | [MIRAGE1002](MIRAGE1002.md) | SyncVar Auto-Property Error | Error | Ensures `[SyncVar]` properties are non-static auto-properties with both getter and setter. | | [MIRAGE1003](MIRAGE1003.md) | Direct Mutation of SyncCollection Elements | Warning | Flags direct modification of elements within SyncCollections because the changes cannot be detected or synced. | | [MIRAGE1004](MIRAGE1004.md) | Reassignment of SyncObject Fields | Error | Restricts reassignment of fields implementing `ISyncObject` (like `SyncList`), requiring them to be marked `readonly`. | +| [MIRAGE1005](MIRAGE1005.md) | Invalid SyncVar Hook Method | Error | Ensures `[SyncVar]` hook methods or events are correctly declared, matched by parameter type, and that hook events are non-static. | | [MIRAGE1101](MIRAGE1101.md) | Misplaced Network Attribute Error | Error | Prevents Mirage network attributes from being declared inside classes that do not inherit from `NetworkBehaviour`. | -| [MIRAGE1201](MIRAGE1201.md) | RPC Signature Error | Error | Disallows generic parameters on RPC methods and enforces valid return types (`void`, `UniTask`, or `UniTask`). | -| [MIRAGE1202](MIRAGE1202.md) | Pass-by-Reference Modifiers in RPCs | Error | Prohibits `ref` or `out` modifiers on RPC parameters since they cannot be serialized over a one-way boundary. | -| [MIRAGE1203](MIRAGE1203.md) | Static RPC Methods | Error | Disallows declaring RPC methods as `static` to preserve the `NetworkBehaviour` instance context. | -| [MIRAGE1204](MIRAGE1204.md) | Invalid ClientRpc Target Configurations | Error | Validates `[ClientRpc]` target settings, ensuring correct return types and connection parameters. | -| [MIRAGE1205](MIRAGE1205.md) | Invalid RateLimit Attribute Settings | Error | Enforces valid positive configurations for interval, refill, and max tokens inside `[RateLimit]` attributes. | -| [MIRAGE1206](MIRAGE1206.md) | Missing RateLimit on ServerRpc | Warning | Recommends decorating `[ServerRpc]` methods with `[RateLimit]` to prevent server denial of service (DoS) attacks. | -| [MIRAGE1301](MIRAGE1301.md) | Message or RPC Class Warning | Warning | Warns about class types used inside network messages or RPC parameters because they cause GC allocations. | -| [MIRAGE1302](MIRAGE1302.md) | Field Type Serialization Validation | Error | Confirms all fields in network messages/RPCs are serializable by Mirage or have registered custom serializers. | +| [MIRAGE1102](MIRAGE1102.md) | Redundant Server/Client Attribute on RPC | Warning | Warns if an RPC method is decorated with both a routing attribute ([ServerRpc]/[ClientRpc]) and an active guard ([Server]/[Client]). | +| [MIRAGE1201](MIRAGE1201.md) | NetworkMessage/RPC Class Warning | Warning | Warns about class types used inside network messages or RPC parameters because they cause GC allocations. | +| [MIRAGE1202](MIRAGE1202.md) | RPC Signature Error | Error | Disallows generic parameters on RPC methods and enforces valid return types (`void`, `UniTask`, or `UniTask`). | +| [MIRAGE1203](MIRAGE1203.md) | Pass-by-Reference Modifiers in RPCs | Error | Prohibits `ref` or `out` modifiers on RPC parameters since they cannot be serialized over a one-way boundary. | +| [MIRAGE1204](MIRAGE1204.md) | Static RPC Methods | Error | Disallows declaring RPC methods as `static` to preserve the `NetworkBehaviour` instance context. | +| [MIRAGE1205](MIRAGE1205.md) | Invalid ClientRpc Target Configurations | Error | Validates `[ClientRpc]` target settings, ensuring correct return types and connection parameters. | +| [MIRAGE1206](MIRAGE1206.md) | Invalid RateLimit Attribute Settings | Error | Enforces valid positive configurations for interval, refill, and max tokens inside `[RateLimit]` attributes. | +| [MIRAGE1207](MIRAGE1207.md) | Missing RateLimit on ServerRpc | Warning | Recommends decorating `[ServerRpc]` methods with `[RateLimit]` to prevent server denial of service (DoS) attacks. | +| [MIRAGE1301](MIRAGE1301.md) | Field Type Serialization Validation | Error | Confirms all fields in network messages/RPCs are serializable by Mirage or have registered custom serializers. | +| [MIRAGE1302](MIRAGE1302.md) | Unserialized Private Field Warning | Warning | (SKIP) Warns if a private field or property in a NetworkMessage will not be serialized. | | [MIRAGE1303](MIRAGE1303.md) | Mismatched Custom Serialization Methods | Error | Requires custom serializers to contain matching, properly-signed reader and writer extension methods. | +| [MIRAGE1304](MIRAGE1304.md) | Non-Serializable MonoBehaviour Parameter | Error | Prohibits passing a plain `MonoBehaviour` (not inheriting from `NetworkBehaviour`) in RPCs or message fields. | +| [MIRAGE1305](MIRAGE1305.md) | Missing NetworkMessage Attribute | Warning | Warns if a type is sent or registered as a message, but lacks the `[NetworkMessage]` attribute. | | [MIRAGE1401](MIRAGE1401.md) | Accessing Network State in Awake/Start | Warning | Warns against accessing network states like `IsServer` during early Unity lifecycle phases. | | [MIRAGE1402](MIRAGE1402.md) | Missing base Call in OnSerialize/OnDeserialize | Warning | Ensures overriding `OnSerialize` or `OnDeserialize` in derived classes calls the base implementation. | | [MIRAGE1501](MIRAGE1501.md) | Network Message Exceeds Safe MTU | Warning | Warns when a message exceeds the safe Maximum Transmission Unit (MTU) to prevent IP fragmentation. | @@ -46,6 +51,9 @@ Modifying the properties of an element inside a `SyncList` or `SyncDictionary` d #### [MIRAGE1004: Reassignment of SyncObject Fields](MIRAGE1004.md) Fields implementing `ISyncObject` (such as `SyncList` or `SyncHashSet`) must be declared as `readonly` and must not be reassigned after construction. Reassigning these fields breaks internal Weaver injection and delta synchronization. To reset the collection, use the collection's `.Clear()` method instead of creating a new instance. +#### [MIRAGE1005: Invalid SyncVar Hook Method](MIRAGE1005.md) +Specifying a hook name in a `[SyncVar]` attribute that cannot be resolved, has mismatched parameter types, or is an unsupported static event triggers a compile-time error. Under automatic overload resolution, ambiguous hook names (matching multiple signatures) will also trigger this error. Correct this by verifying parameter types match the SyncVar's type, making events instance-based, or using explicit `hookType` parameters. + --- ### Group 2: NetworkBehaviour & Attribute Placement @@ -53,47 +61,59 @@ Fields implementing `ISyncObject` (such as `SyncList` or `SyncHashSet`) must be #### [MIRAGE1101: Misplaced Network Attribute Error](MIRAGE1101.md) Mirage-specific attributes like `[SyncVar]`, `[Server]`, `[Client]`, or RPC attributes are only valid within classes inheriting from `NetworkBehaviour`. Placing these on methods, properties, or fields of a regular `MonoBehaviour` or plain C# class will cause a compile-time error. Fix this by ensuring the target class inherits from `NetworkBehaviour`. +#### [MIRAGE1102: Redundant Server/Client Attribute on RPC](MIRAGE1102.md) +Decorating an RPC method with both a routing attribute (`[ServerRpc]` or `[ClientRpc]`) and its corresponding active guard attribute (`[Server]` or `[Client]`) is redundant. For example, `[ServerRpc]` implies the method executes on the server, making `[Server]` redundant. Placing both generates a warning. Resolve this by removing the redundant active guard attribute. + --- ### Group 3: Remote Procedure Calls -#### [MIRAGE1201: RPC Signature Error](MIRAGE1201.md) +#### [MIRAGE1201: NetworkMessage/RPC Class Warning](MIRAGE1201.md) +Using class types inside properties decorated with `[NetworkMessage]` or parameters/returns in RPCs triggers this warning. Reference types cause heap allocations during deserialization and do not support polymorphism. To resolve this, convert the class to a struct, use `[WeaverSafeClass]` with custom serialization, or use `[WeaverSafeClass]` to ignore. + +#### [MIRAGE1202: RPC Signature Error](MIRAGE1202.md) RPC methods (decorated with `[ServerRpc]` or `[ClientRpc]`) cannot be generic and must return `void`, `UniTask`, or `UniTask`. Non-generic signatures and approved return wrappers are mandatory for the Weaver to generate remote routing code. Resolve this by removing generic type parameters and correcting the return type. -#### [MIRAGE1202: Pass-by-Reference Modifiers in RPCs](MIRAGE1202.md) +#### [MIRAGE1203: Pass-by-Reference Modifiers in RPCs](MIRAGE1203.md) Using `ref` or `out` modifiers on RPC method parameters is prohibited because pass-by-reference semantics cannot span a one-way network serialization boundary. All RPC arguments must be passed by value. To share modified state back to the caller, use an async RPC returning `UniTask` or a synchronized `[SyncVar]` property. -#### [MIRAGE1203: Static RPC Methods](MIRAGE1203.md) +#### [MIRAGE1204: Static RPC Methods](MIRAGE1204.md) Declaring an RPC method as `static` causes a compile error because Mirage needs an instance context (`NetworkBehaviour`) to determine the target object identity. Remove the `static` modifier to run the RPC within the instance context of a spawned GameObject. -#### [MIRAGE1204: Invalid ClientRpc Target Configurations](MIRAGE1204.md) +#### [MIRAGE1205: Invalid ClientRpc Target Configurations](MIRAGE1205.md) This rule validates that `[ClientRpc]` target configurations are logically sound. For example, returning values (`UniTask`) is invalid when targeting `RpcTarget.Observers`, and targeting `RpcTarget.Player` requires the first parameter to be an `INetworkPlayer`. Correct the target configuration or adjust the method parameters to resolve the error. -#### [MIRAGE1205: Invalid RateLimit Attribute Settings](MIRAGE1205.md) +#### [MIRAGE1206: Invalid RateLimit Attribute Settings](MIRAGE1206.md) The `[RateLimit]` attribute settings are validated to ensure they configure positive, non-zero values for `Interval`, `Refill`, and `MaxTokens`. Additionally, `MaxTokens` must be greater than or equal to the `Refill` rate to prevent logic loops or server starvation. Update the attribute parameters with valid positive configurations. -#### [MIRAGE1206: Missing RateLimit on ServerRpc](MIRAGE1206.md) +#### [MIRAGE1207: Missing RateLimit on ServerRpc](MIRAGE1207.md) To protect servers against client RPC spamming and potential denial of service (DoS) attacks, all `[ServerRpc]` methods should be protected with a `[RateLimit]` attribute. This warning highlights unprotected methods. Resolve this by applying a `[RateLimit]` attribute specifying appropriate timing and capacity for the method. --- ### Group 4: Serialization -#### [MIRAGE1301: Message or RPC Class Warning](MIRAGE1301.md) -Using class types as fields in a `[NetworkMessage]` or as arguments/return types in RPCs triggers a warning due to garbage collection allocations during deserialization. Standard collections like `List` are ignored, but custom classes should be converted to structs. Alternatively, you can use `[WeaverSafeClass]` with custom read/write extension methods to suppress the warning. - -#### [MIRAGE1302: Field Type Serialization Validation](MIRAGE1302.md) +#### [MIRAGE1301: Field Type Serialization Validation](MIRAGE1301.md) All fields in a `[NetworkMessage]` or parameters in RPCs must be serializable by Mirage. If a type cannot be automatically serialized by the Weaver and lacks registered custom read/write extension methods, compile-time errors occur. Make sure fields use serializable types or implement custom `NetworkWriter` and `NetworkReader` extensions. +#### [MIRAGE1302: Unserialized Private Field Warning](MIRAGE1302.md) +Private fields and properties in a `[NetworkMessage]` are ignored by the Weaver during serialization. This warning alerts developers that these fields will not be synced over the network, helping avoid confusion. Fix this by making the field public or utilizing public properties with backing fields if custom serialization is implemented. + #### [MIRAGE1303: Mismatched Custom Serialization Methods](MIRAGE1303.md) Custom serialization requires registering both a writer extension method and a reader extension method with matching signatures. If one of them is missing or has signature differences, Mirage cannot pair them up, causing compilation failure. To resolve this, ensure both matching methods are fully defined. +#### [MIRAGE1304: Non-Serializable MonoBehaviour Parameter](MIRAGE1304.md) +RPC parameters and `[NetworkMessage]` fields cannot be plain `MonoBehaviour` types. Because plain `MonoBehaviour` components do not inherit from `NetworkBehaviour` and lack a `NetworkIdentity`, Mirage cannot serialize or locate them across the network. Change the class to inherit from `NetworkBehaviour` or use a serializable reference (like `NetworkIdentity`). + +#### [MIRAGE1305: Missing NetworkMessage Attribute](MIRAGE1305.md) +All custom types used to send messages (via `Send()`) or handle messages (via `RegisterHandler()`) must be decorated with the `[NetworkMessage]` attribute. Without it, the Weaver does not generate the necessary message type IDs and serialization code, which leads to runtime errors or unhandled message warnings. + --- ### Group 5: Lifecycles & API Safety #### [MIRAGE1401: Accessing Network State in Awake/Start](MIRAGE1401.md) -Accessing network properties like `IsServer`, `IsClient`, or authority states in Unity's standard `Awake` or `Start` lifecycle methods is unsafe because the network identity is not yet spawned. This leads to incorrect initialization or race conditions. Override `OnStartServer`, `OnStartClient`, or other network-specific start callbacks instead. +Accessing network properties like `IsServer`, `IsClient`, or authority states in Unity's standard `Awake` or `Start` lifecycle methods is unsafe because the network identity is not yet spawned. This leads to incorrect initialization or race conditions. Subscribe to lifecycle events on `Identity` (such as `Identity.OnStartServer`, `Identity.OnStartClient`, `Identity.OnStartLocalPlayer`, or `Identity.OnAuthorityChanged`) during `Awake()` to execute your network initialization code when the network state is fully ready. #### [MIRAGE1402: Missing base Call in OnSerialize/OnDeserialize](MIRAGE1402.md) Derived classes overriding custom `OnSerialize` or `OnDeserialize` methods must call their base class implementations if the base class also synchronizes state. Failing to call the base method prevents base `SyncVars` and properties from synchronizing properly. Fix this by calling the base method and combining their return values. From 56764614625ac658eaf04834ca763a8c6f83976a Mon Sep 17 00:00:00 2001 From: James Frowen Date: Tue, 2 Jun 2026 14:42:37 +0100 Subject: [PATCH 32/41] tests and meta files for samples --- .../Snippets/Analyzers/Mirage1005.cs.meta | 11 ++ .../Snippets/Analyzers/Mirage1102.cs.meta | 11 ++ .../Snippets/Analyzers/Mirage1207.cs.meta | 11 ++ .../Snippets/Analyzers/Mirage1301.cs.meta | 11 ++ .../Snippets/Analyzers/Mirage1302.cs.meta | 11 ++ .../Snippets/Analyzers/Mirage1304.cs.meta | 11 ++ .../Snippets/Analyzers/Mirage1305.cs.meta | 11 ++ .../FieldSerializationTests.cs | 32 ++-- .../RateLimitSettingsTests.cs | 8 +- .../RpcClientTargetTests.cs | 161 ++---------------- .../RpcPassByRefTests.cs | 132 ++------------ .../RpcSignatureTests.cs | 4 +- .../Mirage.Analyzers.Tests/RpcStaticTests.cs | 82 +-------- .../ServerRpcRateLimitMissingTests.cs | 88 +--------- .../SyncObjectReassignmentTests.cs | 131 +------------- .../CustomClientRpcAttributeIgnored.cs | 46 +++++ .../InvalidClientRpcObserversWithUniTask.cs | 37 +++- ...dClientRpcPlayerWithWrongFirstParameter.cs | 40 +++++ ...ientRpcPlayerWithoutConnectionParameter.cs | 31 ++++ .../ValidClientRpcObserversReturnsVoid.cs | 31 ++++ .../ValidClientRpcOwnerWithUniTask.cs | 37 +++- ...lidClientRpcPlayerWithNetworkConnection.cs | 36 +++- .../ValidClientRpcPlayerWithNetworkPlayer.cs | 31 ++++ .../ClientRpcWithOutParameterReportsError.cs | 18 ++ .../ClientRpcWithRefParameterReportsError.cs | 15 ++ ...RpcWithRefParameterDoesNotReportWarning.cs | 21 +++ ...cMethodWithRefOrOutDoesNotReportWarning.cs | 18 ++ .../RpcWithInParameterDoesNotReportWarning.cs | 15 ++ ...lueAndRefParametersDoesNotReportWarning.cs | 18 ++ .../ServerRpcWithOutParameterReportsError.cs | 18 ++ .../ServerRpcWithRefParameterReportsError.cs | 15 ++ .../FakeStaticRpcDoesNotReportWarning.cs | 21 +++ .../InstanceRpcDoesNotReportWarning.cs | 18 ++ .../RpcStatic/StaticClientRpcReportsError.cs | 15 ++ .../StaticNonRpcMethodDoesNotReportWarning.cs | 14 ++ .../RpcStatic/StaticServerRpcReportsError.cs | 15 ++ ...RpcWithoutRateLimitDoesNotReportWarning.cs | 28 +++ ...verRpcWithCustomRateLimitReportsWarning.cs | 34 ++++ ...verRpcWithRateLimitDoesNotReportWarning.cs | 29 ++++ ...ServerRpcWithoutRateLimitReportsWarning.cs | 28 +++ .../NonReadonlySyncObjectReportsError.cs | 20 +++ ...ectFieldNotReadonlyDoesNotReportWarning.cs | 24 +++ .../ReadonlySyncObjectDoesNotReportWarning.cs | 20 +++ ...signedInConstructorDoesNotReportWarning.cs | 25 +++ ...gnmentInLambdaInConstructorReportsError.cs | 29 ++++ ...nLocalFunctionInConstructorReportsError.cs | 29 ++++ ...cObjectReassignmentInMethodReportsError.cs | 25 +++ ...tive_UnboundedFieldAndPropertyInMessage.cs | 30 ++++ .../Negative_UnboundedParameterInRpc.cs | 29 ++++ .../Positive_BoundedStringAndCollection.cs | 34 ++++ .../Positive_NonNetworkContext.cs | 9 + .../Negative_UncompressedFieldsInMessage.cs | 53 ++++++ .../Negative_UncompressedRpcParameters.cs | 55 ++++++ .../Negative_UncompressedSyncVars.cs | 54 ++++++ .../Positive_AllowedUncompressedTypes.cs | 54 ++++++ ...Positive_CompressedPrimitivesAndVectors.cs | 66 +++++++ .../UnboundedCollectionTests.cs | 88 +--------- .../UncompressedPrimitiveTests.cs | 133 +-------------- .../Mirage.Analyzers/MirageRules.cs | 52 +++--- 59 files changed, 1345 insertions(+), 828 deletions(-) create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1005.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1102.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1207.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1301.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1302.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1304.cs.meta create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1305.cs.meta create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/CustomClientRpcAttributeIgnored.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/InvalidClientRpcPlayerWithWrongFirstParameter.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/ClientRpcWithOutParameterReportsError.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/ClientRpcWithRefParameterReportsError.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/FakeRpcWithRefParameterDoesNotReportWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/NonRpcMethodWithRefOrOutDoesNotReportWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/RpcWithInParameterDoesNotReportWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/RpcWithValueAndRefParametersDoesNotReportWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/ServerRpcWithOutParameterReportsError.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/ServerRpcWithRefParameterReportsError.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcStatic/FakeStaticRpcDoesNotReportWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcStatic/InstanceRpcDoesNotReportWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcStatic/StaticClientRpcReportsError.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcStatic/StaticNonRpcMethodDoesNotReportWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcStatic/StaticServerRpcReportsError.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/ServerRpcRateLimitMissing/ClientRpcWithoutRateLimitDoesNotReportWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/ServerRpcRateLimitMissing/ServerRpcWithCustomRateLimitReportsWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/ServerRpcRateLimitMissing/ServerRpcWithRateLimitDoesNotReportWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/ServerRpcRateLimitMissing/ServerRpcWithoutRateLimitReportsWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/NonReadonlySyncObjectReportsError.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/NonSyncObjectFieldNotReadonlyDoesNotReportWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/ReadonlySyncObjectDoesNotReportWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/SyncObjectAssignedInConstructorDoesNotReportWarning.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/SyncObjectReassignmentInLambdaInConstructorReportsError.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/SyncObjectReassignmentInLocalFunctionInConstructorReportsError.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/SyncObjectReassignmentInMethodReportsError.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Negative_UnboundedFieldAndPropertyInMessage.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Negative_UnboundedParameterInRpc.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Positive_BoundedStringAndCollection.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Positive_NonNetworkContext.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Negative_UncompressedFieldsInMessage.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Negative_UncompressedRpcParameters.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Negative_UncompressedSyncVars.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Positive_AllowedUncompressedTypes.cs create mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Positive_CompressedPrimitivesAndVectors.cs diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1005.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1005.cs.meta new file mode 100644 index 00000000000..34dd373744a --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1005.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2b617401c9381274c96828aae67facfa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1102.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1102.cs.meta new file mode 100644 index 00000000000..77bc2795bdf --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1102.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 996f9c64f7931944ab917b44c9f20999 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1207.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1207.cs.meta new file mode 100644 index 00000000000..d664e15885e --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1207.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: de67dfc75dffaca4ebd533475e9ae896 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1301.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1301.cs.meta new file mode 100644 index 00000000000..706875fd625 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1301.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: adef096b2cf08ab48968cff17de54308 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1302.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1302.cs.meta new file mode 100644 index 00000000000..651c1181b94 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1302.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f17fe3396d839284c995fd290652bab6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1304.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1304.cs.meta new file mode 100644 index 00000000000..a602f9e1570 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1304.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 60efbdd7eb5ff014698c492ea9cbce9d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1305.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1305.cs.meta new file mode 100644 index 00000000000..5cccb64a734 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1305.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 916e0ef3509aa654faf0cf8cd7470b8d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/FieldSerializationTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/FieldSerializationTests.cs index 18be82d88ca..3fa9681dd13 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/FieldSerializationTests.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/FieldSerializationTests.cs @@ -31,11 +31,11 @@ public async Task PrivateFieldsAreIgnored() public async Task UnserializableFieldReportsError() { var code = VerifyCS.LoadTestData("FieldSerialization/UnserializableFieldReportsError.cs"); - var expected = VerifyCS.Diagnostic("MIRAGE1302") + var expected = VerifyCS.Diagnostic("MIRAGE1301") .WithLocation(0) .WithArguments("Thread", "NetworkMessage field"); - var expectedClassWarning = VerifyCS.Diagnostic("MIRAGE1301") + var expectedClassWarning = VerifyCS.Diagnostic("MIRAGE1201") .WithLocation(0) .WithArguments("NetworkMessage field", "executionThread", "Thread"); @@ -46,11 +46,11 @@ public async Task UnserializableFieldReportsError() public async Task UnserializablePropertyReportsError() { var code = VerifyCS.LoadTestData("FieldSerialization/UnserializablePropertyReportsError.cs"); - var expected = VerifyCS.Diagnostic("MIRAGE1302") + var expected = VerifyCS.Diagnostic("MIRAGE1301") .WithLocation(0) .WithArguments("Thread", "NetworkMessage property"); - var expectedClassWarning = VerifyCS.Diagnostic("MIRAGE1301") + var expectedClassWarning = VerifyCS.Diagnostic("MIRAGE1201") .WithLocation(0) .WithArguments("NetworkMessage property", "ExecutionThread", "Thread"); @@ -61,11 +61,11 @@ public async Task UnserializablePropertyReportsError() public async Task UnserializableRpcParameterReportsError() { var code = VerifyCS.LoadTestData("FieldSerialization/UnserializableRpcParameterReportsError.cs"); - var expected = VerifyCS.Diagnostic("MIRAGE1302") + var expected = VerifyCS.Diagnostic("MIRAGE1301") .WithLocation(0) .WithArguments("Thread", "RPC parameter"); - var expectedClassWarning = VerifyCS.Diagnostic("MIRAGE1301") + var expectedClassWarning = VerifyCS.Diagnostic("MIRAGE1201") .WithLocation(0) .WithArguments("RPC parameter", "executionThread", "Thread"); @@ -76,11 +76,11 @@ public async Task UnserializableRpcParameterReportsError() public async Task UnserializableRpcReturnTypeReportsError() { var code = VerifyCS.LoadTestData("FieldSerialization/UnserializableRpcReturnTypeReportsError.cs"); - var expected = VerifyCS.Diagnostic("MIRAGE1302") + var expected = VerifyCS.Diagnostic("MIRAGE1301") .WithLocation(0) .WithArguments("Thread", "RPC return type"); - var expectedClassWarning = VerifyCS.Diagnostic("MIRAGE1301") + var expectedClassWarning = VerifyCS.Diagnostic("MIRAGE1201") .WithLocation(0) .WithArguments("RPC return type", "CmdGetSession", "Thread"); @@ -91,7 +91,7 @@ public async Task UnserializableRpcReturnTypeReportsError() public async Task StructWithUnserializableFieldReportsError() { var code = VerifyCS.LoadTestData("FieldSerialization/StructWithUnserializableFieldReportsError.cs"); - var expected = VerifyCS.Diagnostic("MIRAGE1302") + var expected = VerifyCS.Diagnostic("MIRAGE1301") .WithLocation(0) .WithArguments("NestedUnserializable", "NetworkMessage field"); @@ -102,11 +102,11 @@ public async Task StructWithUnserializableFieldReportsError() public async Task RecursiveTypeReportsError() { var code = VerifyCS.LoadTestData("FieldSerialization/RecursiveTypeReportsError.cs"); - var expected1 = VerifyCS.Diagnostic("MIRAGE1302") + var expected1 = VerifyCS.Diagnostic("MIRAGE1301") .WithLocation(0) .WithArguments("RecursiveClass", "NetworkMessage field"); - var expected2 = VerifyCS.Diagnostic("MIRAGE1302") + var expected2 = VerifyCS.Diagnostic("MIRAGE1301") .WithLocation(1) .WithArguments("RecursiveClass", "NetworkMessage field"); @@ -117,7 +117,7 @@ public async Task RecursiveTypeReportsError() public async Task MultiDimensionalArrayReportsError() { var code = VerifyCS.LoadTestData("FieldSerialization/MultiDimensionalArrayReportsError.cs"); - var expected = VerifyCS.Diagnostic("MIRAGE1302") + var expected = VerifyCS.Diagnostic("MIRAGE1301") .WithLocation(0) .WithArguments("Int32", "NetworkMessage field"); @@ -128,7 +128,7 @@ public async Task MultiDimensionalArrayReportsError() public async Task WeaverSafeClassWithUnserializableFieldReportsError() { var code = VerifyCS.LoadTestData("FieldSerialization/WeaverSafeClassWithUnserializableFieldReportsError.cs"); - var expected = VerifyCS.Diagnostic("MIRAGE1302") + var expected = VerifyCS.Diagnostic("MIRAGE1301") .WithLocation(0) .WithArguments("SafeClassWithThread", "NetworkMessage field"); @@ -139,7 +139,7 @@ public async Task WeaverSafeClassWithUnserializableFieldReportsError() public async Task WeaverSafeClassOnFieldSuppressesClassWarning() { var code = VerifyCS.LoadTestData("FieldSerialization/WeaverSafeClassOnFieldSuppressesClassWarning.cs"); - var expected = VerifyCS.Diagnostic("MIRAGE1302") + var expected = VerifyCS.Diagnostic("MIRAGE1301") .WithLocation(0) .WithArguments("Thread", "NetworkMessage field"); @@ -150,7 +150,7 @@ public async Task WeaverSafeClassOnFieldSuppressesClassWarning() public async Task WeaverSafeClassOnPropertySuppressesClassWarning() { var code = VerifyCS.LoadTestData("FieldSerialization/WeaverSafeClassOnPropertySuppressesClassWarning.cs"); - var expected = VerifyCS.Diagnostic("MIRAGE1302") + var expected = VerifyCS.Diagnostic("MIRAGE1301") .WithLocation(0) .WithArguments("Thread", "NetworkMessage property"); @@ -161,7 +161,7 @@ public async Task WeaverSafeClassOnPropertySuppressesClassWarning() public async Task WeaverSafeClassOnParameterSuppressesClassWarning() { var code = VerifyCS.LoadTestData("FieldSerialization/WeaverSafeClassOnParameterSuppressesClassWarning.cs"); - var expected = VerifyCS.Diagnostic("MIRAGE1302") + var expected = VerifyCS.Diagnostic("MIRAGE1301") .WithLocation(0) .WithArguments("Thread", "RPC parameter"); diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/RateLimitSettingsTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/RateLimitSettingsTests.cs index 6455cb6cc42..48c0e498ff7 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/RateLimitSettingsTests.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/RateLimitSettingsTests.cs @@ -24,7 +24,7 @@ public async Task ValidRateLimitDefaultSettings() public async Task InvalidRateLimitInterval() { var code = VerifyCS.LoadTestData("RateLimitSettings/InvalidRateLimitInterval.cs"); - var expected = VerifyCS.Diagnostic("MIRAGE1205") + var expected = VerifyCS.Diagnostic("MIRAGE1206") .WithLocation(0) .WithArguments("CmdFire", "Interval must be greater than zero"); await VerifyCS.VerifyAnalyzerAsync(code, expected); @@ -34,7 +34,7 @@ public async Task InvalidRateLimitInterval() public async Task InvalidRateLimitRefillAndMaxTokens() { var code = VerifyCS.LoadTestData("RateLimitSettings/InvalidRateLimitRefillAndMaxTokens.cs"); - var expected = VerifyCS.Diagnostic("MIRAGE1205") + var expected = VerifyCS.Diagnostic("MIRAGE1206") .WithLocation(0) .WithArguments("CmdFire", "Refill must be greater than zero, MaxTokens must be greater than zero"); await VerifyCS.VerifyAnalyzerAsync(code, expected); @@ -44,7 +44,7 @@ public async Task InvalidRateLimitRefillAndMaxTokens() public async Task InvalidRateLimitMaxTokensLessThanRefill() { var code = VerifyCS.LoadTestData("RateLimitSettings/InvalidRateLimitMaxTokensLessThanRefill.cs"); - var expected = VerifyCS.Diagnostic("MIRAGE1205") + var expected = VerifyCS.Diagnostic("MIRAGE1206") .WithLocation(0) .WithArguments("CmdFire", "MaxTokens must be greater than or equal to Refill"); await VerifyCS.VerifyAnalyzerAsync(code, expected); @@ -61,7 +61,7 @@ public async Task RateLimitOnNonRpcMethodIgnored() public async Task CustomRateLimitAttributeIgnored() { var code = VerifyCS.LoadTestData("RateLimitSettings/CustomRateLimitAttributeIgnored.cs"); - var expected = VerifyCS.Diagnostic("MIRAGE1206") + var expected = VerifyCS.Diagnostic("MIRAGE1207") .WithLocation(0) .WithArguments("CmdFire"); await VerifyCS.VerifyAnalyzerAsync(code, expected); diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/RpcClientTargetTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/RpcClientTargetTests.cs index da3308286af..b30111baeb5 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/RpcClientTargetTests.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/RpcClientTargetTests.cs @@ -6,136 +6,39 @@ namespace Mirage.Analyzers.Tests [TestFixture] public class RpcClientTargetTests { - private const string MockDefinitions = @" -namespace Mirage -{ - public class NetworkBehaviour {} - - public enum RpcTarget - { - Owner = 0, - Observers = 1, - Player = 2 - } - - [System.AttributeUsage(System.AttributeTargets.Method)] - public class ClientRpcAttribute : System.Attribute - { - public RpcTarget target { get; set; } - } - - [System.AttributeUsage(System.AttributeTargets.Method)] - public class ServerRpcAttribute : System.Attribute {} - - public interface INetworkPlayer {} - public class NetworkPlayer : INetworkPlayer {} - public class NetworkConnection {} -} - -namespace Cysharp.Threading.Tasks -{ - public struct UniTask {} - public struct UniTask {} -} -"; - [Test] public async Task ValidClientRpcObserversReturnsVoid() { - // Verify client RPCs default to Observers and allow void returns safely - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - [ClientRpc] - public void RpcUpdate(int value) - { - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("RpcClientTarget/ValidClientRpcObserversReturnsVoid.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task ValidClientRpcPlayerWithNetworkPlayer() { - // Verify targeted client RPCs allow INetworkPlayer as target parameters - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - [ClientRpc(target = RpcTarget.Player)] - public void RpcMessage(INetworkPlayer player, string msg) - { - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("RpcClientTarget/ValidClientRpcPlayerWithNetworkPlayer.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task ValidClientRpcPlayerWithNetworkConnection() { - // Verify targeted client RPCs allow NetworkConnection as target parameters - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - [ClientRpc(target = RpcTarget.Player)] - public void RpcMessage(NetworkConnection conn, string msg) - { - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("RpcClientTarget/ValidClientRpcPlayerWithNetworkConnection.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task ValidClientRpcOwnerWithUniTask() { - // Verify targeted client RPCs to the owner can return UniTask values - var code = @" -using Mirage; -using Cysharp.Threading.Tasks; - -public class MyBehaviour : NetworkBehaviour -{ - [ClientRpc(target = RpcTarget.Owner)] - public UniTask RpcCalculate() - { - return default; - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("RpcClientTarget/ValidClientRpcOwnerWithUniTask.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task InvalidClientRpcObserversWithUniTask() { - // Verify broadcast RPCs cannot return task values because of multi-client response ambiguity - var code = @" -using Mirage; -using Cysharp.Threading.Tasks; - -public class MyBehaviour : NetworkBehaviour -{ - [ClientRpc(target = RpcTarget.Observers)] - public UniTask {|#0:RpcCalculate|}() - { - return default; - } -} -" + MockDefinitions; - - var expected = VerifyCS.Diagnostic("MIRAGE1204") + var code = VerifyCS.LoadTestData("RpcClientTarget/InvalidClientRpcObserversWithUniTask.cs"); + var expected = VerifyCS.Diagnostic("MIRAGE1205") .WithLocation(0) .WithArguments("RpcCalculate", "must return void when target is Observers"); await VerifyCS.VerifyAnalyzerAsync(code, expected); @@ -144,20 +47,8 @@ public class MyBehaviour : NetworkBehaviour [Test] public async Task InvalidClientRpcPlayerWithoutConnectionParameter() { - // Verify targeted client RPCs require a player connection identifier as the first parameter - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - [ClientRpc(target = RpcTarget.Player)] - public void {|#0:RpcMessage|}(string msg) - { - } -} -" + MockDefinitions; - - var expected = VerifyCS.Diagnostic("MIRAGE1204") + var code = VerifyCS.LoadTestData("RpcClientTarget/InvalidClientRpcPlayerWithoutConnectionParameter.cs"); + var expected = VerifyCS.Diagnostic("MIRAGE1205") .WithLocation(0) .WithArguments("RpcMessage", "method with target = Player requires first parameter to be INetworkPlayer or NetworkConnection"); await VerifyCS.VerifyAnalyzerAsync(code, expected); @@ -166,20 +57,8 @@ public class MyBehaviour : NetworkBehaviour [Test] public async Task InvalidClientRpcPlayerWithWrongFirstParameter() { - // Verify targeted client RPCs reject non-connection types for the first parameter - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - [ClientRpc(target = RpcTarget.Player)] - public void {|#0:RpcMessage|}(int connectionId, string msg) - { - } -} -" + MockDefinitions; - - var expected = VerifyCS.Diagnostic("MIRAGE1204") + var code = VerifyCS.LoadTestData("RpcClientTarget/InvalidClientRpcPlayerWithWrongFirstParameter.cs"); + var expected = VerifyCS.Diagnostic("MIRAGE1205") .WithLocation(0) .WithArguments("RpcMessage", "method with target = Player requires first parameter to be INetworkPlayer or NetworkConnection"); await VerifyCS.VerifyAnalyzerAsync(code, expected); @@ -188,25 +67,7 @@ public class MyBehaviour : NetworkBehaviour [Test] public async Task CustomClientRpcAttributeIgnored() { - // Verify the analyzer does not run checks on client RPC attributes defined outside of Mirage - var code = @" -namespace CustomNamespace -{ - public class ClientRpcAttribute : System.Attribute - { - public int target { get; set; } - } -} - -public class MyBehaviour -{ - [CustomNamespace.ClientRpc(target = 2)] - public void RpcMessage(int connectionId) - { - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("RpcClientTarget/CustomClientRpcAttributeIgnored.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } } diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/RpcPassByRefTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/RpcPassByRefTests.cs index ccb28a3a922..40a711ab766 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/RpcPassByRefTests.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/RpcPassByRefTests.cs @@ -6,171 +6,63 @@ namespace Mirage.Analyzers.Tests [TestFixture] public class RpcPassByRefTests { - private const string MockDefinitions = @" -namespace Mirage -{ - public class NetworkBehaviour {} - public class ServerRpcAttribute : System.Attribute {} - public class ClientRpcAttribute : System.Attribute {} - public class RateLimitAttribute : System.Attribute {} -} -"; - [Test] public async Task RpcWithValueAndRefParametersDoesNotReportWarning() { - // Verify that normal RPCs with value parameters or in parameters do not trigger MIRAGE1202 - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - [ServerRpc, RateLimit] - public void CmdDoSomething(int value, string message) {} - - [ClientRpc] - public void RpcDoSomething(int value, string message) {} -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("RpcPassByRef/RpcWithValueAndRefParametersDoesNotReportWarning.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task RpcWithInParameterDoesNotReportWarning() { - // Verify that 'in' parameters (which are read-only references) do not trigger MIRAGE1202 - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - [ServerRpc, RateLimit] - public void CmdDoSomething(in int value) {} -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("RpcPassByRef/RpcWithInParameterDoesNotReportWarning.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task NonRpcMethodWithRefOrOutDoesNotReportWarning() { - // Verify that non-RPC methods can use ref/out parameters freely - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - public void LocalHelper(ref int value, out string result) - { - value += 1; - result = ""done""; - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("RpcPassByRef/NonRpcMethodWithRefOrOutDoesNotReportWarning.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task FakeRpcWithRefParameterDoesNotReportWarning() { - // Verify that a custom attribute with the same name in a different namespace does not trigger MIRAGE1202 - var code = @" -using System; -using Mirage; - -namespace Custom -{ - public class ServerRpcAttribute : Attribute {} -} - -public class MyBehaviour : NetworkBehaviour -{ - [Custom.ServerRpc] - public void CmdFakeRpc(ref int value) {} -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("RpcPassByRef/FakeRpcWithRefParameterDoesNotReportWarning.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task ServerRpcWithRefParameterReportsError() { - // Verify that a ServerRpc with a ref parameter triggers MIRAGE1202 - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - [ServerRpc, RateLimit] - public void CmdDoSomething(ref int {|#0:value|}) {} -} -" + MockDefinitions; - - var expected = VerifyCS.Diagnostic("MIRAGE1202").WithLocation(0).WithArguments("CmdDoSomething", "value"); + var code = VerifyCS.LoadTestData("RpcPassByRef/ServerRpcWithRefParameterReportsError.cs"); + var expected = VerifyCS.Diagnostic("MIRAGE1203").WithLocation(0).WithArguments("CmdDoSomething", "value"); await VerifyCS.VerifyAnalyzerAsync(code, expected); } [Test] public async Task ServerRpcWithOutParameterReportsError() { - // Verify that a ServerRpc with an out parameter triggers MIRAGE1202 - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - [ServerRpc, RateLimit] - public void CmdDoSomething(out int {|#0:value|}) - { - value = 0; - } -} -" + MockDefinitions; - - var expected = VerifyCS.Diagnostic("MIRAGE1202").WithLocation(0).WithArguments("CmdDoSomething", "value"); + var code = VerifyCS.LoadTestData("RpcPassByRef/ServerRpcWithOutParameterReportsError.cs"); + var expected = VerifyCS.Diagnostic("MIRAGE1203").WithLocation(0).WithArguments("CmdDoSomething", "value"); await VerifyCS.VerifyAnalyzerAsync(code, expected); } [Test] public async Task ClientRpcWithRefParameterReportsError() { - // Verify that a ClientRpc with a ref parameter triggers MIRAGE1202 - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - [ClientRpc] - public void RpcDoSomething(ref int {|#0:value|}) {} -} -" + MockDefinitions; - - var expected = VerifyCS.Diagnostic("MIRAGE1202").WithLocation(0).WithArguments("RpcDoSomething", "value"); + var code = VerifyCS.LoadTestData("RpcPassByRef/ClientRpcWithRefParameterReportsError.cs"); + var expected = VerifyCS.Diagnostic("MIRAGE1203").WithLocation(0).WithArguments("RpcDoSomething", "value"); await VerifyCS.VerifyAnalyzerAsync(code, expected); } [Test] public async Task ClientRpcWithOutParameterReportsError() { - // Verify that a ClientRpc with an out parameter triggers MIRAGE1202 - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - [ClientRpc] - public void RpcDoSomething(out int {|#0:value|}) - { - value = 0; - } -} -" + MockDefinitions; - - var expected = VerifyCS.Diagnostic("MIRAGE1202").WithLocation(0).WithArguments("RpcDoSomething", "value"); + var code = VerifyCS.LoadTestData("RpcPassByRef/ClientRpcWithOutParameterReportsError.cs"); + var expected = VerifyCS.Diagnostic("MIRAGE1203").WithLocation(0).WithArguments("RpcDoSomething", "value"); await VerifyCS.VerifyAnalyzerAsync(code, expected); } } diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/RpcSignatureTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/RpcSignatureTests.cs index 75407d35172..dbfc4aca3d6 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/RpcSignatureTests.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/RpcSignatureTests.cs @@ -10,7 +10,7 @@ public class RpcSignatureTests public async Task GenericRpcReportsError() { var code = VerifyCS.LoadTestData("Rpcs/GenericRpc.cs"); - var expected = VerifyCS.Diagnostic("MIRAGE1201") + var expected = VerifyCS.Diagnostic("MIRAGE1202") .WithLocation(0) .WithArguments("CmdGeneric", "cannot have generic parameters"); await VerifyCS.VerifyAnalyzerAsync(code, expected); @@ -20,7 +20,7 @@ public async Task GenericRpcReportsError() public async Task RpcWithInvalidReturnTypeReportsError() { var code = VerifyCS.LoadTestData("Rpcs/RpcWithInvalidReturnType.cs"); - var expected = VerifyCS.Diagnostic("MIRAGE1201") + var expected = VerifyCS.Diagnostic("MIRAGE1202") .WithLocation(0) .WithArguments("CmdReturnsInt", "cannot return 'int' (must return void or UniTask)"); await VerifyCS.VerifyAnalyzerAsync(code, expected); diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/RpcStaticTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/RpcStaticTests.cs index 86109b9735f..3456aec51c2 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/RpcStaticTests.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/RpcStaticTests.cs @@ -6,108 +6,40 @@ namespace Mirage.Analyzers.Tests [TestFixture] public class RpcStaticTests { - private const string MockDefinitions = @" -namespace Mirage -{ - public class NetworkBehaviour {} - public class ServerRpcAttribute : System.Attribute {} - public class ClientRpcAttribute : System.Attribute {} - public class RateLimitAttribute : System.Attribute {} -} -"; - [Test] public async Task InstanceRpcDoesNotReportWarning() { - // Verify that instance-level RPC methods (both ServerRpc and ClientRpc) do not trigger MIRAGE1203 - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - [ServerRpc, RateLimit] - public void CmdDoSomething() {} - - [ClientRpc] - public void RpcDoSomething() {} -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("RpcStatic/InstanceRpcDoesNotReportWarning.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task StaticNonRpcMethodDoesNotReportWarning() { - // Verify that static methods that are not RPCs do not trigger MIRAGE1203 - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - public static void LocalHelper() {} -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("RpcStatic/StaticNonRpcMethodDoesNotReportWarning.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task FakeStaticRpcDoesNotReportWarning() { - // Verify that a fake ServerRpc attribute in a different namespace does not trigger MIRAGE1203 on static methods - var code = @" -using System; -using Mirage; - -namespace Custom -{ - public class ServerRpcAttribute : Attribute {} -} - -public class MyBehaviour : NetworkBehaviour -{ - [Custom.ServerRpc] - public static void CmdFakeRpc() {} -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("RpcStatic/FakeStaticRpcDoesNotReportWarning.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task StaticServerRpcReportsError() { - // Verify that a static ServerRpc method triggers MIRAGE1203 - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - [ServerRpc, RateLimit] - public static void {|#0:CmdDoSomething|}() {} -} -" + MockDefinitions; - - var expected = VerifyCS.Diagnostic("MIRAGE1203").WithLocation(0).WithArguments("CmdDoSomething"); + var code = VerifyCS.LoadTestData("RpcStatic/StaticServerRpcReportsError.cs"); + var expected = VerifyCS.Diagnostic("MIRAGE1204").WithLocation(0).WithArguments("CmdDoSomething"); await VerifyCS.VerifyAnalyzerAsync(code, expected); } [Test] public async Task StaticClientRpcReportsError() { - // Verify that a static ClientRpc method triggers MIRAGE1203 - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - [ClientRpc] - public static void {|#0:RpcDoSomething|}() {} -} -" + MockDefinitions; - - var expected = VerifyCS.Diagnostic("MIRAGE1203").WithLocation(0).WithArguments("RpcDoSomething"); + var code = VerifyCS.LoadTestData("RpcStatic/StaticClientRpcReportsError.cs"); + var expected = VerifyCS.Diagnostic("MIRAGE1204").WithLocation(0).WithArguments("RpcDoSomething"); await VerifyCS.VerifyAnalyzerAsync(code, expected); } } diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/ServerRpcRateLimitMissingTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/ServerRpcRateLimitMissingTests.cs index 05471181538..b88b4a30b40 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/ServerRpcRateLimitMissingTests.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/ServerRpcRateLimitMissingTests.cs @@ -6,83 +6,25 @@ namespace Mirage.Analyzers.Tests [TestFixture] public class ServerRpcRateLimitMissingTests { - private const string MockDefinitions = @" -namespace Mirage -{ - public class NetworkBehaviour {} - - [System.AttributeUsage(System.AttributeTargets.Method)] - public class ServerRpcAttribute : System.Attribute {} - - [System.AttributeUsage(System.AttributeTargets.Method)] - public class ClientRpcAttribute : System.Attribute {} - - [System.AttributeUsage(System.AttributeTargets.Method)] - public class RateLimitAttribute : System.Attribute - { - public float Interval { get; set; } - public int Refill { get; set; } - public int MaxTokens { get; set; } - } -} -"; - [Test] public async Task ServerRpcWithRateLimitDoesNotReportWarning() { - // Verify that server RPCs with a rate limit are considered secure and do not warn - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - [ServerRpc] - [RateLimit(Interval = 1f, Refill = 10, MaxTokens = 10)] - public void CmdInteract() - { - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("ServerRpcRateLimitMissing/ServerRpcWithRateLimitDoesNotReportWarning.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task ClientRpcWithoutRateLimitDoesNotReportWarning() { - // Verify client-bound RPCs do not require rate limits since they run on trusted clients - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - [ClientRpc] - public void RpcUpdateInteract() - { - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("ServerRpcRateLimitMissing/ClientRpcWithoutRateLimitDoesNotReportWarning.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task ServerRpcWithoutRateLimitReportsWarning() { - // Verify that server RPCs without rate limits warn to prevent potential spam attacks - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - [ServerRpc] - public void {|#0:CmdInteract|}() - { - } -} -" + MockDefinitions; - - var expected = VerifyCS.Diagnostic("MIRAGE1206") + var code = VerifyCS.LoadTestData("ServerRpcRateLimitMissing/ServerRpcWithoutRateLimitReportsWarning.cs"); + var expected = VerifyCS.Diagnostic("MIRAGE1207") .WithLocation(0) .WithArguments("CmdInteract"); await VerifyCS.VerifyAnalyzerAsync(code, expected); @@ -91,26 +33,8 @@ public class MyBehaviour : NetworkBehaviour [Test] public async Task ServerRpcWithCustomRateLimitReportsWarning() { - // Verify custom external rate limiting attributes are not recognized by Mirage's defense rule - var code = @" -using Mirage; - -namespace CustomNamespace -{ - public class RateLimitAttribute : System.Attribute {} -} - -public class MyBehaviour : NetworkBehaviour -{ - [ServerRpc] - [CustomNamespace.RateLimit] - public void {|#0:CmdInteract|}() - { - } -} -" + MockDefinitions; - - var expected = VerifyCS.Diagnostic("MIRAGE1206") + var code = VerifyCS.LoadTestData("ServerRpcRateLimitMissing/ServerRpcWithCustomRateLimitReportsWarning.cs"); + var expected = VerifyCS.Diagnostic("MIRAGE1207") .WithLocation(0) .WithArguments("CmdInteract"); await VerifyCS.VerifyAnalyzerAsync(code, expected); diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/SyncObjectReassignmentTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/SyncObjectReassignmentTests.cs index e178de39b00..160869e9834 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/SyncObjectReassignmentTests.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/SyncObjectReassignmentTests.cs @@ -6,95 +6,31 @@ namespace Mirage.Analyzers.Tests [TestFixture] public class SyncObjectReassignmentTests { - private const string MockDefinitions = @" -namespace Mirage -{ - public class NetworkBehaviour {} -} -namespace Mirage.Collections -{ - public interface ISyncObject {} - public class SyncList : ISyncObject - { - public T this[int index] { get => default; set {} } - } -} -"; - [Test] public async Task ReadonlySyncObjectDoesNotReportWarning() { - // Verify that marking an ISyncObject field as readonly does not trigger MIRAGE1004 - var code = @" -using Mirage; -using Mirage.Collections; - -public class MyBehaviour : NetworkBehaviour -{ - public readonly SyncList mySyncList = new SyncList(); -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("SyncObjectReassignment/ReadonlySyncObjectDoesNotReportWarning.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task NonSyncObjectFieldNotReadonlyDoesNotReportWarning() { - // Verify that a regular field not implementing ISyncObject is allowed to not be readonly - var code = @" -using Mirage; - -public class MyBehaviour : NetworkBehaviour -{ - public int myNormalField = 0; - - public void Modify() - { - myNormalField = 10; - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("SyncObjectReassignment/NonSyncObjectFieldNotReadonlyDoesNotReportWarning.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task SyncObjectAssignedInConstructorDoesNotReportWarning() { - // Verify that assigning/initializing ISyncObject in constructor does not trigger MIRAGE1004 - var code = @" -using Mirage; -using Mirage.Collections; - -public class MyBehaviour : NetworkBehaviour -{ - public readonly SyncList mySyncList; - - public MyBehaviour() - { - mySyncList = new SyncList(); - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("SyncObjectReassignment/SyncObjectAssignedInConstructorDoesNotReportWarning.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task NonReadonlySyncObjectReportsError() { - // Verify that an ISyncObject field not marked readonly triggers MIRAGE1004 on field declaration - var code = @" -using Mirage; -using Mirage.Collections; - -public class MyBehaviour : NetworkBehaviour -{ - public SyncList {|#0:mySyncList|}; -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("SyncObjectReassignment/NonReadonlySyncObjectReportsError.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1004").WithLocation(0).WithArguments("mySyncList"); await VerifyCS.VerifyAnalyzerAsync(code, expected); } @@ -102,22 +38,7 @@ public class MyBehaviour : NetworkBehaviour [Test] public async Task SyncObjectReassignmentInMethodReportsError() { - // Verify that reassigning an ISyncObject field outside constructor triggers MIRAGE1004 - var code = @" -using Mirage; -using Mirage.Collections; - -public class MyBehaviour : NetworkBehaviour -{ - public readonly SyncList mySyncList = new SyncList(); - - public void ResetList() - { - {|#0:mySyncList|} = new SyncList(); - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("SyncObjectReassignment/SyncObjectReassignmentInMethodReportsError.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1004").WithLocation(0).WithArguments("mySyncList"); await VerifyCS.VerifyAnalyzerAsync(code, expected); } @@ -125,26 +46,7 @@ public void ResetList() [Test] public async Task SyncObjectReassignmentInLocalFunctionInConstructorReportsError() { - // Verify that reassigning an ISyncObject inside a local function inside constructor triggers MIRAGE1004 - var code = @" -using Mirage; -using Mirage.Collections; - -public class MyBehaviour : NetworkBehaviour -{ - public readonly SyncList mySyncList = new SyncList(); - - public MyBehaviour() - { - void LocalMethod() - { - {|#0:mySyncList|} = new SyncList(); - } - LocalMethod(); - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("SyncObjectReassignment/SyncObjectReassignmentInLocalFunctionInConstructorReportsError.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1004").WithLocation(0).WithArguments("mySyncList"); await VerifyCS.VerifyAnalyzerAsync(code, expected); } @@ -152,26 +54,7 @@ void LocalMethod() [Test] public async Task SyncObjectReassignmentInLambdaInConstructorReportsError() { - // Verify that reassigning an ISyncObject inside a lambda expression inside constructor triggers MIRAGE1004 - var code = @" -using System; -using Mirage; -using Mirage.Collections; - -public class MyBehaviour : NetworkBehaviour -{ - public readonly SyncList mySyncList = new SyncList(); - - public MyBehaviour() - { - Action act = () => { - {|#0:mySyncList|} = new SyncList(); - }; - act(); - } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("SyncObjectReassignment/SyncObjectReassignmentInLambdaInConstructorReportsError.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1004").WithLocation(0).WithArguments("mySyncList"); await VerifyCS.VerifyAnalyzerAsync(code, expected); } diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/CustomClientRpcAttributeIgnored.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/CustomClientRpcAttributeIgnored.cs new file mode 100644 index 00000000000..bc221025a6e --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/CustomClientRpcAttributeIgnored.cs @@ -0,0 +1,46 @@ +namespace CustomNamespace +{ + public class ClientRpcAttribute : System.Attribute + { + public int target { get; set; } + } +} + +public class MyBehaviour +{ + [CustomNamespace.ClientRpc(target = 2)] + public void RpcMessage(int connectionId) + { + } +} + +namespace Mirage +{ + public class NetworkBehaviour {} + + public enum RpcTarget + { + Owner = 0, + Observers = 1, + Player = 2 + } + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ClientRpcAttribute : System.Attribute + { + public RpcTarget target { get; set; } + } + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ServerRpcAttribute : System.Attribute {} + + public interface INetworkPlayer {} + public class NetworkPlayer : INetworkPlayer {} + public class NetworkConnection {} +} + +namespace Cysharp.Threading.Tasks +{ + public struct UniTask {} + public struct UniTask {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/InvalidClientRpcObserversWithUniTask.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/InvalidClientRpcObserversWithUniTask.cs index d1e4ad09a15..391d13add0d 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/InvalidClientRpcObserversWithUniTask.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/InvalidClientRpcObserversWithUniTask.cs @@ -1,12 +1,6 @@ using Mirage; using Cysharp.Threading.Tasks; -namespace Cysharp.Threading.Tasks -{ - public struct UniTask {} - public struct UniTask {} -} - public class MyBehaviour : NetworkBehaviour { [ClientRpc(target = RpcTarget.Observers)] @@ -15,3 +9,34 @@ public class MyBehaviour : NetworkBehaviour return default; } } + +namespace Mirage +{ + public class NetworkBehaviour {} + + public enum RpcTarget + { + Owner = 0, + Observers = 1, + Player = 2 + } + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ClientRpcAttribute : System.Attribute + { + public RpcTarget target { get; set; } + } + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ServerRpcAttribute : System.Attribute {} + + public interface INetworkPlayer {} + public class NetworkPlayer : INetworkPlayer {} + public class NetworkConnection {} +} + +namespace Cysharp.Threading.Tasks +{ + public struct UniTask {} + public struct UniTask {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/InvalidClientRpcPlayerWithWrongFirstParameter.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/InvalidClientRpcPlayerWithWrongFirstParameter.cs new file mode 100644 index 00000000000..5b8640e1286 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/InvalidClientRpcPlayerWithWrongFirstParameter.cs @@ -0,0 +1,40 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ClientRpc(target = RpcTarget.Player)] + public void {|#0:RpcMessage|}(int connectionId, string msg) + { + } +} + +namespace Mirage +{ + public class NetworkBehaviour {} + + public enum RpcTarget + { + Owner = 0, + Observers = 1, + Player = 2 + } + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ClientRpcAttribute : System.Attribute + { + public RpcTarget target { get; set; } + } + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ServerRpcAttribute : System.Attribute {} + + public interface INetworkPlayer {} + public class NetworkPlayer : INetworkPlayer {} + public class NetworkConnection {} +} + +namespace Cysharp.Threading.Tasks +{ + public struct UniTask {} + public struct UniTask {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/InvalidClientRpcPlayerWithoutConnectionParameter.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/InvalidClientRpcPlayerWithoutConnectionParameter.cs index f10ec0c7ef0..c5f317ae159 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/InvalidClientRpcPlayerWithoutConnectionParameter.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/InvalidClientRpcPlayerWithoutConnectionParameter.cs @@ -7,3 +7,34 @@ public class MyBehaviour : NetworkBehaviour { } } + +namespace Mirage +{ + public class NetworkBehaviour {} + + public enum RpcTarget + { + Owner = 0, + Observers = 1, + Player = 2 + } + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ClientRpcAttribute : System.Attribute + { + public RpcTarget target { get; set; } + } + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ServerRpcAttribute : System.Attribute {} + + public interface INetworkPlayer {} + public class NetworkPlayer : INetworkPlayer {} + public class NetworkConnection {} +} + +namespace Cysharp.Threading.Tasks +{ + public struct UniTask {} + public struct UniTask {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcObserversReturnsVoid.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcObserversReturnsVoid.cs index 77945d5fa06..3cb2738b46b 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcObserversReturnsVoid.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcObserversReturnsVoid.cs @@ -7,3 +7,34 @@ public void RpcUpdate(int value) { } } + +namespace Mirage +{ + public class NetworkBehaviour {} + + public enum RpcTarget + { + Owner = 0, + Observers = 1, + Player = 2 + } + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ClientRpcAttribute : System.Attribute + { + public RpcTarget target { get; set; } + } + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ServerRpcAttribute : System.Attribute {} + + public interface INetworkPlayer {} + public class NetworkPlayer : INetworkPlayer {} + public class NetworkConnection {} +} + +namespace Cysharp.Threading.Tasks +{ + public struct UniTask {} + public struct UniTask {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcOwnerWithUniTask.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcOwnerWithUniTask.cs index f933139f88a..0ebb1faf2a3 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcOwnerWithUniTask.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcOwnerWithUniTask.cs @@ -1,12 +1,6 @@ using Mirage; using Cysharp.Threading.Tasks; -namespace Cysharp.Threading.Tasks -{ - public struct UniTask {} - public struct UniTask {} -} - public class MyBehaviour : NetworkBehaviour { [ClientRpc(target = RpcTarget.Owner)] @@ -15,3 +9,34 @@ public UniTask RpcCalculate() return default; } } + +namespace Mirage +{ + public class NetworkBehaviour {} + + public enum RpcTarget + { + Owner = 0, + Observers = 1, + Player = 2 + } + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ClientRpcAttribute : System.Attribute + { + public RpcTarget target { get; set; } + } + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ServerRpcAttribute : System.Attribute {} + + public interface INetworkPlayer {} + public class NetworkPlayer : INetworkPlayer {} + public class NetworkConnection {} +} + +namespace Cysharp.Threading.Tasks +{ + public struct UniTask {} + public struct UniTask {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcPlayerWithNetworkConnection.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcPlayerWithNetworkConnection.cs index 76c88a9da5b..043ca98fca3 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcPlayerWithNetworkConnection.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcPlayerWithNetworkConnection.cs @@ -1,10 +1,5 @@ using Mirage; -namespace Mirage -{ - public class NetworkConnection {} -} - public class MyBehaviour : NetworkBehaviour { [ClientRpc(target = RpcTarget.Player)] @@ -12,3 +7,34 @@ public void RpcMessage(NetworkConnection conn, string msg) { } } + +namespace Mirage +{ + public class NetworkBehaviour {} + + public enum RpcTarget + { + Owner = 0, + Observers = 1, + Player = 2 + } + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ClientRpcAttribute : System.Attribute + { + public RpcTarget target { get; set; } + } + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ServerRpcAttribute : System.Attribute {} + + public interface INetworkPlayer {} + public class NetworkPlayer : INetworkPlayer {} + public class NetworkConnection {} +} + +namespace Cysharp.Threading.Tasks +{ + public struct UniTask {} + public struct UniTask {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcPlayerWithNetworkPlayer.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcPlayerWithNetworkPlayer.cs index 3670aa2ed60..cbc1f384f37 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcPlayerWithNetworkPlayer.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcClientTarget/ValidClientRpcPlayerWithNetworkPlayer.cs @@ -7,3 +7,34 @@ public void RpcMessage(INetworkPlayer player, string msg) { } } + +namespace Mirage +{ + public class NetworkBehaviour {} + + public enum RpcTarget + { + Owner = 0, + Observers = 1, + Player = 2 + } + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ClientRpcAttribute : System.Attribute + { + public RpcTarget target { get; set; } + } + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ServerRpcAttribute : System.Attribute {} + + public interface INetworkPlayer {} + public class NetworkPlayer : INetworkPlayer {} + public class NetworkConnection {} +} + +namespace Cysharp.Threading.Tasks +{ + public struct UniTask {} + public struct UniTask {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/ClientRpcWithOutParameterReportsError.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/ClientRpcWithOutParameterReportsError.cs new file mode 100644 index 00000000000..fcfc5760d9c --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/ClientRpcWithOutParameterReportsError.cs @@ -0,0 +1,18 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ClientRpc] + public void RpcDoSomething(out int {|#0:value|}) + { + value = 0; + } +} + +namespace Mirage +{ + public class NetworkBehaviour {} + public class ServerRpcAttribute : System.Attribute {} + public class ClientRpcAttribute : System.Attribute {} + public class RateLimitAttribute : System.Attribute {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/ClientRpcWithRefParameterReportsError.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/ClientRpcWithRefParameterReportsError.cs new file mode 100644 index 00000000000..a98867a8735 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/ClientRpcWithRefParameterReportsError.cs @@ -0,0 +1,15 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ClientRpc] + public void RpcDoSomething(ref int {|#0:value|}) {} +} + +namespace Mirage +{ + public class NetworkBehaviour {} + public class ServerRpcAttribute : System.Attribute {} + public class ClientRpcAttribute : System.Attribute {} + public class RateLimitAttribute : System.Attribute {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/FakeRpcWithRefParameterDoesNotReportWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/FakeRpcWithRefParameterDoesNotReportWarning.cs new file mode 100644 index 00000000000..ed3ac57bcf6 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/FakeRpcWithRefParameterDoesNotReportWarning.cs @@ -0,0 +1,21 @@ +using System; +using Mirage; + +namespace Custom +{ + public class ServerRpcAttribute : Attribute {} +} + +public class MyBehaviour : NetworkBehaviour +{ + [Custom.ServerRpc] + public void CmdFakeRpc(ref int value) {} +} + +namespace Mirage +{ + public class NetworkBehaviour {} + public class ServerRpcAttribute : System.Attribute {} + public class ClientRpcAttribute : System.Attribute {} + public class RateLimitAttribute : System.Attribute {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/NonRpcMethodWithRefOrOutDoesNotReportWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/NonRpcMethodWithRefOrOutDoesNotReportWarning.cs new file mode 100644 index 00000000000..2f6117b92e7 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/NonRpcMethodWithRefOrOutDoesNotReportWarning.cs @@ -0,0 +1,18 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + public void LocalHelper(ref int value, out string result) + { + value += 1; + result = "done"; + } +} + +namespace Mirage +{ + public class NetworkBehaviour {} + public class ServerRpcAttribute : System.Attribute {} + public class ClientRpcAttribute : System.Attribute {} + public class RateLimitAttribute : System.Attribute {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/RpcWithInParameterDoesNotReportWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/RpcWithInParameterDoesNotReportWarning.cs new file mode 100644 index 00000000000..81f27ad75f7 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/RpcWithInParameterDoesNotReportWarning.cs @@ -0,0 +1,15 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc, RateLimit] + public void CmdDoSomething(in int value) {} +} + +namespace Mirage +{ + public class NetworkBehaviour {} + public class ServerRpcAttribute : System.Attribute {} + public class ClientRpcAttribute : System.Attribute {} + public class RateLimitAttribute : System.Attribute {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/RpcWithValueAndRefParametersDoesNotReportWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/RpcWithValueAndRefParametersDoesNotReportWarning.cs new file mode 100644 index 00000000000..d62a26bc2a5 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/RpcWithValueAndRefParametersDoesNotReportWarning.cs @@ -0,0 +1,18 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc, RateLimit] + public void CmdDoSomething(int value, string message) {} + + [ClientRpc] + public void RpcDoSomething(int value, string message) {} +} + +namespace Mirage +{ + public class NetworkBehaviour {} + public class ServerRpcAttribute : System.Attribute {} + public class ClientRpcAttribute : System.Attribute {} + public class RateLimitAttribute : System.Attribute {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/ServerRpcWithOutParameterReportsError.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/ServerRpcWithOutParameterReportsError.cs new file mode 100644 index 00000000000..504e7725054 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/ServerRpcWithOutParameterReportsError.cs @@ -0,0 +1,18 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc, RateLimit] + public void CmdDoSomething(out int {|#0:value|}) + { + value = 0; + } +} + +namespace Mirage +{ + public class NetworkBehaviour {} + public class ServerRpcAttribute : System.Attribute {} + public class ClientRpcAttribute : System.Attribute {} + public class RateLimitAttribute : System.Attribute {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/ServerRpcWithRefParameterReportsError.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/ServerRpcWithRefParameterReportsError.cs new file mode 100644 index 00000000000..2b407a2224a --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcPassByRef/ServerRpcWithRefParameterReportsError.cs @@ -0,0 +1,15 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc, RateLimit] + public void CmdDoSomething(ref int {|#0:value|}) {} +} + +namespace Mirage +{ + public class NetworkBehaviour {} + public class ServerRpcAttribute : System.Attribute {} + public class ClientRpcAttribute : System.Attribute {} + public class RateLimitAttribute : System.Attribute {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcStatic/FakeStaticRpcDoesNotReportWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcStatic/FakeStaticRpcDoesNotReportWarning.cs new file mode 100644 index 00000000000..b5b8cabf6fc --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcStatic/FakeStaticRpcDoesNotReportWarning.cs @@ -0,0 +1,21 @@ +using System; +using Mirage; + +namespace Custom +{ + public class ServerRpcAttribute : Attribute {} +} + +public class MyBehaviour : NetworkBehaviour +{ + [Custom.ServerRpc] + public static void CmdFakeRpc() {} +} + +namespace Mirage +{ + public class NetworkBehaviour {} + public class ServerRpcAttribute : System.Attribute {} + public class ClientRpcAttribute : System.Attribute {} + public class RateLimitAttribute : System.Attribute {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcStatic/InstanceRpcDoesNotReportWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcStatic/InstanceRpcDoesNotReportWarning.cs new file mode 100644 index 00000000000..1c9a33c3ce8 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcStatic/InstanceRpcDoesNotReportWarning.cs @@ -0,0 +1,18 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc, RateLimit] + public void CmdDoSomething() {} + + [ClientRpc] + public void RpcDoSomething() {} +} + +namespace Mirage +{ + public class NetworkBehaviour {} + public class ServerRpcAttribute : System.Attribute {} + public class ClientRpcAttribute : System.Attribute {} + public class RateLimitAttribute : System.Attribute {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcStatic/StaticClientRpcReportsError.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcStatic/StaticClientRpcReportsError.cs new file mode 100644 index 00000000000..d03c19b366f --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcStatic/StaticClientRpcReportsError.cs @@ -0,0 +1,15 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ClientRpc] + public static void {|#0:RpcDoSomething|}() {} +} + +namespace Mirage +{ + public class NetworkBehaviour {} + public class ServerRpcAttribute : System.Attribute {} + public class ClientRpcAttribute : System.Attribute {} + public class RateLimitAttribute : System.Attribute {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcStatic/StaticNonRpcMethodDoesNotReportWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcStatic/StaticNonRpcMethodDoesNotReportWarning.cs new file mode 100644 index 00000000000..48a62ffda36 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcStatic/StaticNonRpcMethodDoesNotReportWarning.cs @@ -0,0 +1,14 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + public static void LocalHelper() {} +} + +namespace Mirage +{ + public class NetworkBehaviour {} + public class ServerRpcAttribute : System.Attribute {} + public class ClientRpcAttribute : System.Attribute {} + public class RateLimitAttribute : System.Attribute {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcStatic/StaticServerRpcReportsError.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcStatic/StaticServerRpcReportsError.cs new file mode 100644 index 00000000000..3d331654c2a --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/RpcStatic/StaticServerRpcReportsError.cs @@ -0,0 +1,15 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc, RateLimit] + public static void {|#0:CmdDoSomething|}() {} +} + +namespace Mirage +{ + public class NetworkBehaviour {} + public class ServerRpcAttribute : System.Attribute {} + public class ClientRpcAttribute : System.Attribute {} + public class RateLimitAttribute : System.Attribute {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/ServerRpcRateLimitMissing/ClientRpcWithoutRateLimitDoesNotReportWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/ServerRpcRateLimitMissing/ClientRpcWithoutRateLimitDoesNotReportWarning.cs new file mode 100644 index 00000000000..24cce74011e --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/ServerRpcRateLimitMissing/ClientRpcWithoutRateLimitDoesNotReportWarning.cs @@ -0,0 +1,28 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ClientRpc] + public void RpcUpdateInteract() + { + } +} + +namespace Mirage +{ + public class NetworkBehaviour {} + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ServerRpcAttribute : System.Attribute {} + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ClientRpcAttribute : System.Attribute {} + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class RateLimitAttribute : System.Attribute + { + public float Interval { get; set; } + public int Refill { get; set; } + public int MaxTokens { get; set; } + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/ServerRpcRateLimitMissing/ServerRpcWithCustomRateLimitReportsWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/ServerRpcRateLimitMissing/ServerRpcWithCustomRateLimitReportsWarning.cs new file mode 100644 index 00000000000..09971f7b036 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/ServerRpcRateLimitMissing/ServerRpcWithCustomRateLimitReportsWarning.cs @@ -0,0 +1,34 @@ +using Mirage; + +namespace CustomNamespace +{ + public class RateLimitAttribute : System.Attribute {} +} + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc] + [CustomNamespace.RateLimit] + public void {|#0:CmdInteract|}() + { + } +} + +namespace Mirage +{ + public class NetworkBehaviour {} + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ServerRpcAttribute : System.Attribute {} + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ClientRpcAttribute : System.Attribute {} + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class RateLimitAttribute : System.Attribute + { + public float Interval { get; set; } + public int Refill { get; set; } + public int MaxTokens { get; set; } + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/ServerRpcRateLimitMissing/ServerRpcWithRateLimitDoesNotReportWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/ServerRpcRateLimitMissing/ServerRpcWithRateLimitDoesNotReportWarning.cs new file mode 100644 index 00000000000..3f062571f82 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/ServerRpcRateLimitMissing/ServerRpcWithRateLimitDoesNotReportWarning.cs @@ -0,0 +1,29 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc] + [RateLimit(Interval = 1f, Refill = 10, MaxTokens = 10)] + public void CmdInteract() + { + } +} + +namespace Mirage +{ + public class NetworkBehaviour {} + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ServerRpcAttribute : System.Attribute {} + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ClientRpcAttribute : System.Attribute {} + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class RateLimitAttribute : System.Attribute + { + public float Interval { get; set; } + public int Refill { get; set; } + public int MaxTokens { get; set; } + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/ServerRpcRateLimitMissing/ServerRpcWithoutRateLimitReportsWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/ServerRpcRateLimitMissing/ServerRpcWithoutRateLimitReportsWarning.cs new file mode 100644 index 00000000000..60ec5f89ea8 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/ServerRpcRateLimitMissing/ServerRpcWithoutRateLimitReportsWarning.cs @@ -0,0 +1,28 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + [ServerRpc] + public void {|#0:CmdInteract|}() + { + } +} + +namespace Mirage +{ + public class NetworkBehaviour {} + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ServerRpcAttribute : System.Attribute {} + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class ClientRpcAttribute : System.Attribute {} + + [System.AttributeUsage(System.AttributeTargets.Method)] + public class RateLimitAttribute : System.Attribute + { + public float Interval { get; set; } + public int Refill { get; set; } + public int MaxTokens { get; set; } + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/NonReadonlySyncObjectReportsError.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/NonReadonlySyncObjectReportsError.cs new file mode 100644 index 00000000000..9093ed18a51 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/NonReadonlySyncObjectReportsError.cs @@ -0,0 +1,20 @@ +using Mirage; +using Mirage.Collections; + +public class MyBehaviour : NetworkBehaviour +{ + public SyncList {|#0:mySyncList|}; +} + +namespace Mirage +{ + public class NetworkBehaviour {} +} +namespace Mirage.Collections +{ + public interface ISyncObject {} + public class SyncList : ISyncObject + { + public T this[int index] { get => default; set {} } + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/NonSyncObjectFieldNotReadonlyDoesNotReportWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/NonSyncObjectFieldNotReadonlyDoesNotReportWarning.cs new file mode 100644 index 00000000000..89335c3d61a --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/NonSyncObjectFieldNotReadonlyDoesNotReportWarning.cs @@ -0,0 +1,24 @@ +using Mirage; + +public class MyBehaviour : NetworkBehaviour +{ + public int myNormalField = 0; + + public void Modify() + { + myNormalField = 10; + } +} + +namespace Mirage +{ + public class NetworkBehaviour {} +} +namespace Mirage.Collections +{ + public interface ISyncObject {} + public class SyncList : ISyncObject + { + public T this[int index] { get => default; set {} } + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/ReadonlySyncObjectDoesNotReportWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/ReadonlySyncObjectDoesNotReportWarning.cs new file mode 100644 index 00000000000..38987800556 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/ReadonlySyncObjectDoesNotReportWarning.cs @@ -0,0 +1,20 @@ +using Mirage; +using Mirage.Collections; + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncList mySyncList = new SyncList(); +} + +namespace Mirage +{ + public class NetworkBehaviour {} +} +namespace Mirage.Collections +{ + public interface ISyncObject {} + public class SyncList : ISyncObject + { + public T this[int index] { get => default; set {} } + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/SyncObjectAssignedInConstructorDoesNotReportWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/SyncObjectAssignedInConstructorDoesNotReportWarning.cs new file mode 100644 index 00000000000..2f2d4515a3b --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/SyncObjectAssignedInConstructorDoesNotReportWarning.cs @@ -0,0 +1,25 @@ +using Mirage; +using Mirage.Collections; + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncList mySyncList; + + public MyBehaviour() + { + mySyncList = new SyncList(); + } +} + +namespace Mirage +{ + public class NetworkBehaviour {} +} +namespace Mirage.Collections +{ + public interface ISyncObject {} + public class SyncList : ISyncObject + { + public T this[int index] { get => default; set {} } + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/SyncObjectReassignmentInLambdaInConstructorReportsError.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/SyncObjectReassignmentInLambdaInConstructorReportsError.cs new file mode 100644 index 00000000000..3197f2a4ccb --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/SyncObjectReassignmentInLambdaInConstructorReportsError.cs @@ -0,0 +1,29 @@ +using System; +using Mirage; +using Mirage.Collections; + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncList mySyncList = new SyncList(); + + public MyBehaviour() + { + Action act = () => { + {|#0:mySyncList|} = new SyncList(); + }; + act(); + } +} + +namespace Mirage +{ + public class NetworkBehaviour {} +} +namespace Mirage.Collections +{ + public interface ISyncObject {} + public class SyncList : ISyncObject + { + public T this[int index] { get => default; set {} } + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/SyncObjectReassignmentInLocalFunctionInConstructorReportsError.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/SyncObjectReassignmentInLocalFunctionInConstructorReportsError.cs new file mode 100644 index 00000000000..1ea4fbcf542 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/SyncObjectReassignmentInLocalFunctionInConstructorReportsError.cs @@ -0,0 +1,29 @@ +using Mirage; +using Mirage.Collections; + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncList mySyncList = new SyncList(); + + public MyBehaviour() + { + void LocalMethod() + { + {|#0:mySyncList|} = new SyncList(); + } + LocalMethod(); + } +} + +namespace Mirage +{ + public class NetworkBehaviour {} +} +namespace Mirage.Collections +{ + public interface ISyncObject {} + public class SyncList : ISyncObject + { + public T this[int index] { get => default; set {} } + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/SyncObjectReassignmentInMethodReportsError.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/SyncObjectReassignmentInMethodReportsError.cs new file mode 100644 index 00000000000..a12404bf680 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/SyncObjectReassignment/SyncObjectReassignmentInMethodReportsError.cs @@ -0,0 +1,25 @@ +using Mirage; +using Mirage.Collections; + +public class MyBehaviour : NetworkBehaviour +{ + public readonly SyncList mySyncList = new SyncList(); + + public void ResetList() + { + {|#0:mySyncList|} = new SyncList(); + } +} + +namespace Mirage +{ + public class NetworkBehaviour {} +} +namespace Mirage.Collections +{ + public interface ISyncObject {} + public class SyncList : ISyncObject + { + public T this[int index] { get => default; set {} } + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Negative_UnboundedFieldAndPropertyInMessage.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Negative_UnboundedFieldAndPropertyInMessage.cs new file mode 100644 index 00000000000..df34cbfdb59 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Negative_UnboundedFieldAndPropertyInMessage.cs @@ -0,0 +1,30 @@ +using Mirage; +using System.Collections.Generic; + +[NetworkMessage] +public struct InvalidMessage +{ + public string {|#0:Name|}; + + public int[] {|#1:Scores|}; + + public List {|#2:Positions|} { get; set; } +} + +namespace Mirage +{ + public class NetworkMessageAttribute : System.Attribute {} + public class NetworkBehaviour {} + public class ServerRpcAttribute : System.Attribute {} + public class ClientRpcAttribute : System.Attribute {} +} +namespace Mirage.Serialization +{ + public class BitCountAttribute : System.Attribute + { + public BitCountAttribute(int bits) {} + } + public class VarIntAttribute : System.Attribute {} + public class BitCountFromRangeAttribute : System.Attribute {} + public class VarIntBlocksAttribute : System.Attribute {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Negative_UnboundedParameterInRpc.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Negative_UnboundedParameterInRpc.cs new file mode 100644 index 00000000000..e523c9a6f2c --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Negative_UnboundedParameterInRpc.cs @@ -0,0 +1,29 @@ +using Mirage; +using System.Collections.Generic; + +public class PlayerBehaviour : NetworkBehaviour +{ + [ServerRpc] + public void CmdSendText(string {|#0:text|}) {} + + [ClientRpc] + public void RpcUpdateList(List {|#1:items|}) {} +} + +namespace Mirage +{ + public class NetworkMessageAttribute : System.Attribute {} + public class NetworkBehaviour {} + public class ServerRpcAttribute : System.Attribute {} + public class ClientRpcAttribute : System.Attribute {} +} +namespace Mirage.Serialization +{ + public class BitCountAttribute : System.Attribute + { + public BitCountAttribute(int bits) {} + } + public class VarIntAttribute : System.Attribute {} + public class BitCountFromRangeAttribute : System.Attribute {} + public class VarIntBlocksAttribute : System.Attribute {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Positive_BoundedStringAndCollection.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Positive_BoundedStringAndCollection.cs new file mode 100644 index 00000000000..f8709e2cf34 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Positive_BoundedStringAndCollection.cs @@ -0,0 +1,34 @@ +using Mirage; +using Mirage.Serialization; +using System.Collections.Generic; + +[NetworkMessage] +public struct ValidMessage +{ + [BitCount(8)] + public string Name; + + [VarInt] + public int[] Scores; + + [BitCount(10)] + public List Positions; +} + +namespace Mirage +{ + public class NetworkMessageAttribute : System.Attribute {} + public class NetworkBehaviour {} + public class ServerRpcAttribute : System.Attribute {} + public class ClientRpcAttribute : System.Attribute {} +} +namespace Mirage.Serialization +{ + public class BitCountAttribute : System.Attribute + { + public BitCountAttribute(int bits) {} + } + public class VarIntAttribute : System.Attribute {} + public class BitCountFromRangeAttribute : System.Attribute {} + public class VarIntBlocksAttribute : System.Attribute {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Positive_NonNetworkContext.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Positive_NonNetworkContext.cs new file mode 100644 index 00000000000..7609fcca31d --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Positive_NonNetworkContext.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +public class StandardClass +{ + public string UnboundedString; + public List UnboundedList; + + public void NormalMethod(string param) {} +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Negative_UncompressedFieldsInMessage.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Negative_UncompressedFieldsInMessage.cs new file mode 100644 index 00000000000..634f8a94dc1 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Negative_UncompressedFieldsInMessage.cs @@ -0,0 +1,53 @@ +using Mirage; +using UnityEngine; + +[NetworkMessage] +public struct StatusMessage +{ + public double {|#0:timestamp|}; + public Vector2 {|#1:offset|}; +} + +namespace Mirage +{ + public class NetworkMessageAttribute : System.Attribute {} + public class NetworkBehaviour {} + public class SyncVarAttribute : System.Attribute {} + public class ServerRpcAttribute : System.Attribute {} + public class ClientRpcAttribute : System.Attribute {} +} +namespace Mirage.Serialization +{ + public class BitCountAttribute : System.Attribute + { + public BitCountAttribute(int bits) {} + } + public class BitCountFromRangeAttribute : System.Attribute {} + public class VarIntAttribute : System.Attribute {} + public class VarIntBlocksAttribute : System.Attribute {} + public class FloatPackAttribute : System.Attribute {} + public class Vector2PackAttribute : System.Attribute {} + public class Vector3PackAttribute : System.Attribute {} + public class QuaternionPackAttribute : System.Attribute {} +} +namespace UnityEngine +{ + public struct Vector2 + { + public float x; + public float y; + } + public struct Vector3 + { + public float x; + public float y; + public float z; + } + public struct Quaternion + { + public float x; + public float y; + public float z; + public float w; + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Negative_UncompressedRpcParameters.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Negative_UncompressedRpcParameters.cs new file mode 100644 index 00000000000..5cc21fcd0fe --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Negative_UncompressedRpcParameters.cs @@ -0,0 +1,55 @@ +using Mirage; +using UnityEngine; + +public class Player : NetworkBehaviour +{ + [ServerRpc] + public void CmdUpdateStatus(int {|#0:score|}, float {|#1:val|}) {} + + [ClientRpc] + public void RpcUpdatePhysics(Vector3 {|#2:pos|}, Quaternion {|#3:rot|}) {} +} + +namespace Mirage +{ + public class NetworkMessageAttribute : System.Attribute {} + public class NetworkBehaviour {} + public class SyncVarAttribute : System.Attribute {} + public class ServerRpcAttribute : System.Attribute {} + public class ClientRpcAttribute : System.Attribute {} +} +namespace Mirage.Serialization +{ + public class BitCountAttribute : System.Attribute + { + public BitCountAttribute(int bits) {} + } + public class BitCountFromRangeAttribute : System.Attribute {} + public class VarIntAttribute : System.Attribute {} + public class VarIntBlocksAttribute : System.Attribute {} + public class FloatPackAttribute : System.Attribute {} + public class Vector2PackAttribute : System.Attribute {} + public class Vector3PackAttribute : System.Attribute {} + public class QuaternionPackAttribute : System.Attribute {} +} +namespace UnityEngine +{ + public struct Vector2 + { + public float x; + public float y; + } + public struct Vector3 + { + public float x; + public float y; + public float z; + } + public struct Quaternion + { + public float x; + public float y; + public float z; + public float w; + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Negative_UncompressedSyncVars.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Negative_UncompressedSyncVars.cs new file mode 100644 index 00000000000..42dc8ca9ed2 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Negative_UncompressedSyncVars.cs @@ -0,0 +1,54 @@ +using Mirage; + +public class Player : NetworkBehaviour +{ + [SyncVar] + public int {|#0:Health|} { get; set; } + + [SyncVar] + public float {|#1:PlayerScale|}; +} + +namespace Mirage +{ + public class NetworkMessageAttribute : System.Attribute {} + public class NetworkBehaviour {} + public class SyncVarAttribute : System.Attribute {} + public class ServerRpcAttribute : System.Attribute {} + public class ClientRpcAttribute : System.Attribute {} +} +namespace Mirage.Serialization +{ + public class BitCountAttribute : System.Attribute + { + public BitCountAttribute(int bits) {} + } + public class BitCountFromRangeAttribute : System.Attribute {} + public class VarIntAttribute : System.Attribute {} + public class VarIntBlocksAttribute : System.Attribute {} + public class FloatPackAttribute : System.Attribute {} + public class Vector2PackAttribute : System.Attribute {} + public class Vector3PackAttribute : System.Attribute {} + public class QuaternionPackAttribute : System.Attribute {} +} +namespace UnityEngine +{ + public struct Vector2 + { + public float x; + public float y; + } + public struct Vector3 + { + public float x; + public float y; + public float z; + } + public struct Quaternion + { + public float x; + public float y; + public float z; + public float w; + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Positive_AllowedUncompressedTypes.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Positive_AllowedUncompressedTypes.cs new file mode 100644 index 00000000000..f5e91650145 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Positive_AllowedUncompressedTypes.cs @@ -0,0 +1,54 @@ +using Mirage; + +[NetworkMessage] +public struct AllowedMessage +{ + public bool isReady; + public byte health; + public char category; + public short level; +} + +namespace Mirage +{ + public class NetworkMessageAttribute : System.Attribute {} + public class NetworkBehaviour {} + public class SyncVarAttribute : System.Attribute {} + public class ServerRpcAttribute : System.Attribute {} + public class ClientRpcAttribute : System.Attribute {} +} +namespace Mirage.Serialization +{ + public class BitCountAttribute : System.Attribute + { + public BitCountAttribute(int bits) {} + } + public class BitCountFromRangeAttribute : System.Attribute {} + public class VarIntAttribute : System.Attribute {} + public class VarIntBlocksAttribute : System.Attribute {} + public class FloatPackAttribute : System.Attribute {} + public class Vector2PackAttribute : System.Attribute {} + public class Vector3PackAttribute : System.Attribute {} + public class QuaternionPackAttribute : System.Attribute {} +} +namespace UnityEngine +{ + public struct Vector2 + { + public float x; + public float y; + } + public struct Vector3 + { + public float x; + public float y; + public float z; + } + public struct Quaternion + { + public float x; + public float y; + public float z; + public float w; + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Positive_CompressedPrimitivesAndVectors.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Positive_CompressedPrimitivesAndVectors.cs new file mode 100644 index 00000000000..7a6e4914c59 --- /dev/null +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Positive_CompressedPrimitivesAndVectors.cs @@ -0,0 +1,66 @@ +using Mirage; +using Mirage.Serialization; +using UnityEngine; + +[NetworkMessage] +public struct ValidMessage +{ + [BitCount(8)] + public int score; + + [FloatPack] + public float temperature; + + [Vector2Pack] + public Vector2 velocity; + + [Vector3Pack] + public Vector3 position; + + [QuaternionPack] + public Quaternion rotation; +} + +namespace Mirage +{ + public class NetworkMessageAttribute : System.Attribute {} + public class NetworkBehaviour {} + public class SyncVarAttribute : System.Attribute {} + public class ServerRpcAttribute : System.Attribute {} + public class ClientRpcAttribute : System.Attribute {} +} +namespace Mirage.Serialization +{ + public class BitCountAttribute : System.Attribute + { + public BitCountAttribute(int bits) {} + } + public class BitCountFromRangeAttribute : System.Attribute {} + public class VarIntAttribute : System.Attribute {} + public class VarIntBlocksAttribute : System.Attribute {} + public class FloatPackAttribute : System.Attribute {} + public class Vector2PackAttribute : System.Attribute {} + public class Vector3PackAttribute : System.Attribute {} + public class QuaternionPackAttribute : System.Attribute {} +} +namespace UnityEngine +{ + public struct Vector2 + { + public float x; + public float y; + } + public struct Vector3 + { + public float x; + public float y; + public float z; + } + public struct Quaternion + { + public float x; + public float y; + public float z; + public float w; + } +} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/UnboundedCollectionTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/UnboundedCollectionTests.cs index 3f5cb664d1a..3bce17e762a 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/UnboundedCollectionTests.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/UnboundedCollectionTests.cs @@ -6,90 +6,24 @@ namespace Mirage.Analyzers.Tests [TestFixture] public class UnboundedCollectionTests { - private const string MockDefinitions = @" -namespace Mirage -{ - public class NetworkMessageAttribute : System.Attribute {} - public class NetworkBehaviour {} - public class ServerRpcAttribute : System.Attribute {} - public class ClientRpcAttribute : System.Attribute {} -} -namespace Mirage.Serialization -{ - public class BitCountAttribute : System.Attribute - { - public BitCountAttribute(int bits) {} - } - public class VarIntAttribute : System.Attribute {} - public class BitCountFromRangeAttribute : System.Attribute {} - public class VarIntBlocksAttribute : System.Attribute {} -} -"; - [Test] public async Task Positive_BoundedStringAndCollection() { - // Verify that strings and collections with a bit packing attribute do not trigger a warning - var code = @" -using Mirage; -using Mirage.Serialization; -using System.Collections.Generic; - -[NetworkMessage] -public struct ValidMessage -{ - [BitCount(8)] - public string Name; - - [VarInt] - public int[] Scores; - - [BitCount(10)] - public List Positions; -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("UnboundedCollection/Positive_BoundedStringAndCollection.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task Positive_NonNetworkContext() { - // Verify that unbounded strings/collections outside of NetworkMessages or RPCs do not trigger a warning - var code = @" -using System.Collections.Generic; - -public class StandardClass -{ - public string UnboundedString; - public List UnboundedList; - - public void NormalMethod(string param) {} -} -"; - + var code = VerifyCS.LoadTestData("UnboundedCollection/Positive_NonNetworkContext.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task Negative_UnboundedFieldAndPropertyInMessage() { - // Verify that unbounded string/collection fields and properties in a NetworkMessage trigger warning - var code = @" -using Mirage; -using System.Collections.Generic; - -[NetworkMessage] -public struct InvalidMessage -{ - public string {|#0:Name|}; - - public int[] {|#1:Scores|}; - - public List {|#2:Positions|} { get; set; } -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("UnboundedCollection/Negative_UnboundedFieldAndPropertyInMessage.cs"); var expectedField1 = VerifyCS.Diagnostic("MIRAGE1502").WithLocation(0).WithArguments("Name", "String"); var expectedField2 = VerifyCS.Diagnostic("MIRAGE1502").WithLocation(1).WithArguments("Scores", "Int32[]"); var expectedProp = VerifyCS.Diagnostic("MIRAGE1502").WithLocation(2).WithArguments("Positions", "List"); @@ -100,21 +34,7 @@ public struct InvalidMessage [Test] public async Task Negative_UnboundedParameterInRpc() { - // Verify that unbounded parameters in ServerRpc and ClientRpc trigger warnings - var code = @" -using Mirage; -using System.Collections.Generic; - -public class PlayerBehaviour : NetworkBehaviour -{ - [ServerRpc] - public void CmdSendText(string {|#0:text|}) {} - - [ClientRpc] - public void RpcUpdateList(List {|#1:items|}) {} -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("UnboundedCollection/Negative_UnboundedParameterInRpc.cs"); var expectedParam1 = VerifyCS.Diagnostic("MIRAGE1502").WithLocation(0).WithArguments("text", "String"); var expectedParam2 = VerifyCS.Diagnostic("MIRAGE1502").WithLocation(1).WithArguments("items", "List"); diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/UncompressedPrimitiveTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/UncompressedPrimitiveTests.cs index b07ce7bce3c..7fe4ed2f6fc 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/UncompressedPrimitiveTests.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/UncompressedPrimitiveTests.cs @@ -6,121 +6,24 @@ namespace Mirage.Analyzers.Tests [TestFixture] public class UncompressedPrimitiveTests { - private const string MockDefinitions = @" -namespace Mirage -{ - public class NetworkMessageAttribute : System.Attribute {} - public class NetworkBehaviour {} - public class SyncVarAttribute : System.Attribute {} - public class ServerRpcAttribute : System.Attribute {} - public class ClientRpcAttribute : System.Attribute {} -} -namespace Mirage.Serialization -{ - public class BitCountAttribute : System.Attribute - { - public BitCountAttribute(int bits) {} - } - public class BitCountFromRangeAttribute : System.Attribute {} - public class VarIntAttribute : System.Attribute {} - public class VarIntBlocksAttribute : System.Attribute {} - public class FloatPackAttribute : System.Attribute {} - public class Vector2PackAttribute : System.Attribute {} - public class Vector3PackAttribute : System.Attribute {} - public class QuaternionPackAttribute : System.Attribute {} -} -namespace UnityEngine -{ - public struct Vector2 - { - public float x; - public float y; - } - public struct Vector3 - { - public float x; - public float y; - public float z; - } - public struct Quaternion - { - public float x; - public float y; - public float z; - public float w; - } -} -"; - [Test] public async Task Positive_CompressedPrimitivesAndVectors() { - // Verify that decorated primitive types and Unity vectors do not trigger uncompressed warning - var code = @" -using Mirage; -using Mirage.Serialization; -using UnityEngine; - -[NetworkMessage] -public struct ValidMessage -{ - [BitCount(8)] - public int score; - - [FloatPack] - public float temperature; - - [Vector2Pack] - public Vector2 velocity; - - [Vector3Pack] - public Vector3 position; - - [QuaternionPack] - public Quaternion rotation; -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("UncompressedPrimitive/Positive_CompressedPrimitivesAndVectors.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task Positive_AllowedUncompressedTypes() { - // Verify that small type primitives like bool, byte, sbyte, char, short, ushort do not require compression - var code = @" -using Mirage; - -[NetworkMessage] -public struct AllowedMessage -{ - public bool isReady; - public byte health; - public char category; - public short level; -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("UncompressedPrimitive/Positive_AllowedUncompressedTypes.cs"); await VerifyCS.VerifyAnalyzerAsync(code); } [Test] public async Task Negative_UncompressedSyncVars() { - // Verify that uncompressed SyncVar properties and fields trigger warning - var code = @" -using Mirage; - -public class Player : NetworkBehaviour -{ - [SyncVar] - public int {|#0:Health|} { get; set; } - - [SyncVar] - public float {|#1:PlayerScale|}; -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("UncompressedPrimitive/Negative_UncompressedSyncVars.cs"); var expectedProp = VerifyCS.Diagnostic("MIRAGE1503").WithLocation(0).WithArguments("Health", "Int32"); var expectedField = VerifyCS.Diagnostic("MIRAGE1503").WithLocation(1).WithArguments("PlayerScale", "Single"); @@ -130,21 +33,7 @@ public class Player : NetworkBehaviour [Test] public async Task Negative_UncompressedRpcParameters() { - // Verify that uncompressed RPC parameters trigger warning - var code = @" -using Mirage; -using UnityEngine; - -public class Player : NetworkBehaviour -{ - [ServerRpc] - public void CmdUpdateStatus(int {|#0:score|}, float {|#1:val|}) {} - - [ClientRpc] - public void RpcUpdatePhysics(Vector3 {|#2:pos|}, Quaternion {|#3:rot|}) {} -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("UncompressedPrimitive/Negative_UncompressedRpcParameters.cs"); var expectedScore = VerifyCS.Diagnostic("MIRAGE1503").WithLocation(0).WithArguments("score", "Int32"); var expectedVal = VerifyCS.Diagnostic("MIRAGE1503").WithLocation(1).WithArguments("val", "Single"); var expectedPos = VerifyCS.Diagnostic("MIRAGE1503").WithLocation(2).WithArguments("pos", "Vector3"); @@ -156,19 +45,7 @@ public void RpcUpdatePhysics(Vector3 {|#2:pos|}, Quaternion {|#3:rot|}) {} [Test] public async Task Negative_UncompressedFieldsInMessage() { - // Verify that uncompressed fields in a NetworkMessage trigger warning - var code = @" -using Mirage; -using UnityEngine; - -[NetworkMessage] -public struct StatusMessage -{ - public double {|#0:timestamp|}; - public Vector2 {|#1:offset|}; -} -" + MockDefinitions; - + var code = VerifyCS.LoadTestData("UncompressedPrimitive/Negative_UncompressedFieldsInMessage.cs"); var expectedTimestamp = VerifyCS.Diagnostic("MIRAGE1503").WithLocation(0).WithArguments("timestamp", "Double"); var expectedOffset = VerifyCS.Diagnostic("MIRAGE1503").WithLocation(1).WithArguments("offset", "Vector2"); diff --git a/CSharpAnalyzers/Mirage.Analyzers/MirageRules.cs b/CSharpAnalyzers/Mirage.Analyzers/MirageRules.cs index 706ad445414..67247d68eef 100644 --- a/CSharpAnalyzers/Mirage.Analyzers/MirageRules.cs +++ b/CSharpAnalyzers/Mirage.Analyzers/MirageRules.cs @@ -10,14 +10,14 @@ public static class MirageRules public const string DirectMutationDiagnosticId = "MIRAGE1003"; public const string ReassignmentDiagnosticId = "MIRAGE1004"; public const string NetworkBehaviourAttributeDiagnosticId = "MIRAGE1101"; - public const string RpcSignatureDiagnosticId = "MIRAGE1201"; - public const string RpcRefOutDiagnosticId = "MIRAGE1202"; - public const string RpcStaticDiagnosticId = "MIRAGE1203"; - public const string ClientRpcTargetDiagnosticId = "MIRAGE1204"; - public const string RateLimitSettingsDiagnosticId = "MIRAGE1205"; - public const string ServerRpcMissingRateLimitDiagnosticId = "MIRAGE1206"; - public const string MessageOrRpcDiagnosticId = "MIRAGE1301"; - public const string FieldTypeSerializationDiagnosticId = "MIRAGE1302"; + public const string MessageOrRpcDiagnosticId = "MIRAGE1201"; + public const string RpcSignatureDiagnosticId = "MIRAGE1202"; + public const string RpcRefOutDiagnosticId = "MIRAGE1203"; + public const string RpcStaticDiagnosticId = "MIRAGE1204"; + public const string ClientRpcTargetDiagnosticId = "MIRAGE1205"; + public const string RateLimitSettingsDiagnosticId = "MIRAGE1206"; + public const string ServerRpcMissingRateLimitDiagnosticId = "MIRAGE1207"; + public const string FieldTypeSerializationDiagnosticId = "MIRAGE1301"; public const string MismatchedSerializationDiagnosticId = "MIRAGE1303"; public const string LifecycleNetworkStateDiagnosticId = "MIRAGE1401"; public const string LifecycleMissingBaseCallDiagnosticId = "MIRAGE1402"; @@ -75,6 +75,16 @@ public static class MirageRules description: "Network attributes like SyncVar, Server, Client, HasAuthority, LocalPlayer, ServerRpc, ClientRpc, and NetworkMethod are only valid inside NetworkBehaviour classes.", helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1101"); + public static readonly DiagnosticDescriptor MessageOrRpcRule = new DiagnosticDescriptor( + MessageOrRpcDiagnosticId, + "Class type used in NetworkMessage or RPC without WeaverSafeClass attribute", + "{0} '{1}' is a class type '{2}'. Class-based types allocate memory upon deserialization and do not support polymorphism (only declared fields serialize). Consider using a struct, implementing custom serialization and marking the class with [WeaverSafeClass], or decorating this member/parameter with [WeaverSafeClass] to ignore.", + "Usage", + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: "Class types used as NetworkMessage fields or RPC parameters/returns should be value types or marked with [WeaverSafeClass] to avoid allocations and polymorphism bugs.", + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1201"); + public static readonly DiagnosticDescriptor RpcSignatureRule = new DiagnosticDescriptor( RpcSignatureDiagnosticId, "RPC method must be non-generic and return void or UniTask", @@ -83,7 +93,7 @@ public static class MirageRules DiagnosticSeverity.Error, isEnabledByDefault: true, description: "Methods marked with ServerRpc or ClientRpc cannot be generic and must return void or UniTask.", - helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1201"); + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1202"); public static readonly DiagnosticDescriptor RpcRefOutRule = new DiagnosticDescriptor( RpcRefOutDiagnosticId, @@ -93,7 +103,7 @@ public static class MirageRules DiagnosticSeverity.Error, isEnabledByDefault: true, description: "RPC parameters cannot be pass-by-reference (ref/out).", - helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1202"); + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1203"); public static readonly DiagnosticDescriptor RpcStaticRule = new DiagnosticDescriptor( RpcStaticDiagnosticId, @@ -103,7 +113,7 @@ public static class MirageRules DiagnosticSeverity.Error, isEnabledByDefault: true, description: "RPC methods cannot be static.", - helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1203"); + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1204"); public static readonly DiagnosticDescriptor ClientRpcTargetRule = new DiagnosticDescriptor( ClientRpcTargetDiagnosticId, @@ -113,7 +123,7 @@ public static class MirageRules DiagnosticSeverity.Error, isEnabledByDefault: true, description: "ClientRpc target configurations must be valid.", - helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1204"); + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1205"); public static readonly DiagnosticDescriptor RateLimitSettingsRule = new DiagnosticDescriptor( RateLimitSettingsDiagnosticId, @@ -123,7 +133,7 @@ public static class MirageRules DiagnosticSeverity.Error, isEnabledByDefault: true, description: "RateLimit parameters must be positive and MaxTokens >= Refill.", - helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1205"); + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1206"); public static readonly DiagnosticDescriptor ServerRpcMissingRateLimitRule = new DiagnosticDescriptor( ServerRpcMissingRateLimitDiagnosticId, @@ -133,17 +143,7 @@ public static class MirageRules DiagnosticSeverity.Warning, isEnabledByDefault: true, description: "Every ServerRpc should be protected by a [RateLimit] attribute.", - helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1206"); - - public static readonly DiagnosticDescriptor MessageOrRpcRule = new DiagnosticDescriptor( - MessageOrRpcDiagnosticId, - "Class type used in NetworkMessage or RPC without WeaverSafeClass attribute", - "{0} '{1}' is a class type '{2}'. Class-based types allocate memory upon deserialization and do not support polymorphism (only declared fields serialize). Consider using a struct, implementing custom serialization and marking the class with [WeaverSafeClass], or decorating this member/parameter with [WeaverSafeClass] to ignore.", - "Usage", - DiagnosticSeverity.Warning, - isEnabledByDefault: true, - description: "Class types used as NetworkMessage fields or RPC parameters/returns should be value types or marked with [WeaverSafeClass] to avoid allocations and polymorphism bugs.", - helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1301"); + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1207"); public static readonly DiagnosticDescriptor FieldTypeSerializationRule = new DiagnosticDescriptor( FieldTypeSerializationDiagnosticId, @@ -153,7 +153,7 @@ public static class MirageRules DiagnosticSeverity.Error, isEnabledByDefault: true, description: "All fields in NetworkMessages and parameters in RPCs must be serializable by Mirage.", - helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1302"); + helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1301"); public static readonly DiagnosticDescriptor MismatchedSerializationRule = new DiagnosticDescriptor( MismatchedSerializationDiagnosticId, @@ -221,13 +221,13 @@ public static class MirageRules DirectMutationRule, ReassignmentRule, NetworkBehaviourAttributeRule, + MessageOrRpcRule, RpcSignatureRule, RpcRefOutRule, RpcStaticRule, ClientRpcTargetRule, RateLimitSettingsRule, ServerRpcMissingRateLimitRule, - MessageOrRpcRule, FieldTypeSerializationRule, MismatchedSerializationRule, LifecycleNetworkStateRule, From bd9acca94ea68fad93e1a271035629800efd2ee5 Mon Sep 17 00:00:00 2001 From: James Frowen Date: Tue, 2 Jun 2026 14:50:36 +0100 Subject: [PATCH 33/41] feat(Weaver): getting line context when attributes are used on wrong type --- .../Logging/WeaverException.cs | 4 +- .../Serialization/ValueSerializerFinder.cs | 59 ++++++++++++++++--- Assets/Mirage/Weaver/SerializeFunctionBase.cs | 4 +- Assets/Mirage/Weaver/Weaver.cs | 5 ++ 4 files changed, 61 insertions(+), 11 deletions(-) diff --git a/Assets/Mirage/Weaver/Mirage.CecilExtensions/Logging/WeaverException.cs b/Assets/Mirage/Weaver/Mirage.CecilExtensions/Logging/WeaverException.cs index aa87a34f1f5..5c5c9f7ef4d 100644 --- a/Assets/Mirage/Weaver/Mirage.CecilExtensions/Logging/WeaverException.cs +++ b/Assets/Mirage/Weaver/Mirage.CecilExtensions/Logging/WeaverException.cs @@ -17,8 +17,8 @@ namespace Mirage.CodeGen // should be caught within weaver and returned to user using DiagnosticMessage public class WeaverException : Exception { - public readonly SequencePoint SequencePoint; - public readonly MemberReference MemberReference; + public SequencePoint SequencePoint { get; set; } + public MemberReference MemberReference { get; set; } public WeaverException(string message, MemberReference memberReference, SequencePoint sequencePoint) : base(message) { diff --git a/Assets/Mirage/Weaver/Serialization/ValueSerializerFinder.cs b/Assets/Mirage/Weaver/Serialization/ValueSerializerFinder.cs index 95e062485ad..ea841ff14d5 100644 --- a/Assets/Mirage/Weaver/Serialization/ValueSerializerFinder.cs +++ b/Assets/Mirage/Weaver/Serialization/ValueSerializerFinder.cs @@ -1,8 +1,10 @@ using System; +using System.Linq; using Mirage.CodeGen; using Mirage.Serialization; using Mirage.Weaver.SyncVars; using Mono.Cecil; +using Mono.Cecil.Cil; namespace Mirage.Weaver.Serialization { @@ -67,15 +69,58 @@ public static ValueSerializer GetSerializer(ModuleDefinition module, TypeDefinit // if user adds 2 attributes that dont work together weaver should then throw error ValueSerializer valueSerializer = null; - // attributeProvider is null for generic fields, - // but that is find because they wont have any of these attributes anyway - if (attributeProvider != null) - valueSerializer = GetUsingAttribute(module, holder, attributeProvider, fieldType, fieldName, writers, readers, valueSerializer); + try + { + // attributeProvider is null for generic fields, + // but that is find because they wont have any of these attributes anyway + if (attributeProvider != null) + valueSerializer = GetUsingAttribute(module, holder, attributeProvider, fieldType, fieldName, writers, readers, valueSerializer); - if (valueSerializer == null) - valueSerializer = FindSerializeFunctions(writers, readers, fieldType); + if (valueSerializer == null) + valueSerializer = FindSerializeFunctions(writers, readers, fieldType); - return valueSerializer; + return valueSerializer; + } + catch (WeaverException e) + { + if (e.MemberReference == null && attributeProvider != null) + { + var (mr, sp) = GetContext(attributeProvider); + e.MemberReference = mr; + e.SequencePoint = sp; + } + throw; + } + } + + private static (MemberReference, SequencePoint) GetContext(ICustomAttributeProvider provider) + { + if (provider is MemberReference mr) + { + var sp = GetSequencePoint(mr); + return (mr, sp); + } + if (provider is ParameterDefinition pd) + { + var method = pd.Method as MethodDefinition; + if (method == null && pd.Method is MethodReference methodRef) + method = methodRef.Resolve(); + + var sp = method?.GetFirstSequencePoint(); + return (method, sp); + } + return (null, null); + } + + private static SequencePoint GetSequencePoint(MemberReference mr) + { + if (mr is MethodDefinition md) + return md.GetFirstSequencePoint(); + if (mr is PropertyDefinition pd) + return pd.GetMethod?.GetFirstSequencePoint() ?? pd.SetMethod?.GetFirstSequencePoint(); + if (mr is TypeDefinition td) + return td.Methods.FirstOrDefault(m => m.DebugInformation.SequencePoints.Any())?.GetFirstSequencePoint(); + return null; } private static ValueSerializer GetUsingAttribute(ModuleDefinition module, TypeDefinition holder, ICustomAttributeProvider attributeProvider, TypeReference fieldType, string fieldName, Writers writers, Readers readers, ValueSerializer valueSerializer) diff --git a/Assets/Mirage/Weaver/SerializeFunctionBase.cs b/Assets/Mirage/Weaver/SerializeFunctionBase.cs index 9edf162757e..305e34f0dc7 100644 --- a/Assets/Mirage/Weaver/SerializeFunctionBase.cs +++ b/Assets/Mirage/Weaver/SerializeFunctionBase.cs @@ -101,14 +101,14 @@ public MethodReference TryGetFunction(SequencePoint sequencePoint) => /// /// /// - /// found methohd or null + /// found method or null public MethodReference TryGetFunction(TypeReference typeReference, SequencePoint sequencePoint) { try { return GetFunction_Throws(typeReference); } - catch (SerializeFunctionException e) + catch (WeaverException e) { logger.Error(e, sequencePoint); return null; diff --git a/Assets/Mirage/Weaver/Weaver.cs b/Assets/Mirage/Weaver/Weaver.cs index 1e34a035b68..11a2617855c 100644 --- a/Assets/Mirage/Weaver/Weaver.cs +++ b/Assets/Mirage/Weaver/Weaver.cs @@ -79,6 +79,11 @@ protected override ResultType Process(AssemblyDefinition assembly, ICompiledAsse return ResultType.Success; } + catch (WeaverException e) + { + logger.Error(e); + return ResultType.Failed; + } catch (Exception e) { logger.Error("Exception :" + e); From 0affa17c04317e4cdd2945f5f87346f12f01af4b Mon Sep 17 00:00:00 2001 From: James Frowen Date: Tue, 2 Jun 2026 14:50:59 +0100 Subject: [PATCH 34/41] size checking rule --- .../Samples~/Snippets/Analyzers/Mirage1501.cs | 25 ++--- .../Samples~/Snippets/Analyzers/Mirage1502.cs | 30 ------ .../Snippets/Analyzers/Mirage1502.cs.meta | 11 -- .../Samples~/Snippets/Analyzers/Mirage1503.cs | 37 ------- .../Snippets/Analyzers/Mirage1503.cs.meta | 11 -- .../MtuSizeEstimationTests.cs | 25 +++-- .../Edge_RecursiveStructOrClassRef.cs | 2 +- .../Positive_LargeMessageReducedByPacking.cs | 2 +- ...itive_SmallMessageDoesNotTriggerWarning.cs | 2 +- ...tive_UnboundedFieldAndPropertyInMessage.cs | 30 ------ .../Negative_UnboundedParameterInRpc.cs | 29 ----- .../Positive_BoundedStringAndCollection.cs | 34 ------ .../Positive_NonNetworkContext.cs | 9 -- .../Negative_UncompressedFieldsInMessage.cs | 53 --------- .../Negative_UncompressedRpcParameters.cs | 55 ---------- .../Negative_UncompressedSyncVars.cs | 54 ---------- .../Positive_AllowedUncompressedTypes.cs | 54 ---------- ...Positive_CompressedPrimitivesAndVectors.cs | 66 ------------ .../UnboundedCollectionTests.cs | 44 -------- .../UncompressedPrimitiveTests.cs | 55 ---------- .../MirageAnalyzer.Performance.cs | 102 +----------------- .../Mirage.Analyzers/MirageAnalyzer.cs | 3 - .../Mirage.Analyzers/MirageRules.cs | 40 ++----- CSharpAnalyzers/analyzers_roadmap.md | 6 +- doc/docs/analyzers/MIRAGE1501.md | 21 ++-- doc/docs/analyzers/MIRAGE1502.md | 19 ---- doc/docs/analyzers/MIRAGE1503.md | 19 ---- doc/docs/analyzers/index.md | 16 +-- 28 files changed, 53 insertions(+), 801 deletions(-) delete mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1502.cs delete mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1502.cs.meta delete mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1503.cs delete mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1503.cs.meta delete mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Negative_UnboundedFieldAndPropertyInMessage.cs delete mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Negative_UnboundedParameterInRpc.cs delete mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Positive_BoundedStringAndCollection.cs delete mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Positive_NonNetworkContext.cs delete mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Negative_UncompressedFieldsInMessage.cs delete mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Negative_UncompressedRpcParameters.cs delete mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Negative_UncompressedSyncVars.cs delete mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Positive_AllowedUncompressedTypes.cs delete mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Positive_CompressedPrimitivesAndVectors.cs delete mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/UnboundedCollectionTests.cs delete mode 100644 CSharpAnalyzers/Mirage.Analyzers.Tests/UncompressedPrimitiveTests.cs delete mode 100644 doc/docs/analyzers/MIRAGE1502.md delete mode 100644 doc/docs/analyzers/MIRAGE1503.md diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1501.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1501.cs index b82118df15e..576f4031f57 100644 --- a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1501.cs +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1501.cs @@ -3,29 +3,16 @@ namespace Mirage.Snippets.Analyzers { - namespace M1501.Triggering + namespace M1501.Example { - // CodeEmbed-Start: mirage1501-triggering + // CodeEmbed-Start: mirage1501-example [NetworkMessage] - public struct LargeTelemetryMessage + public struct PlayerUpdateMessage { - // Warning: Array of Vector3 has estimated size of 1536 bytes, exceeding the 1200-byte MTU limit. - public Vector3[] historicalTransforms; - } - // CodeEmbed-End: mirage1501-triggering - } - - namespace M1501.Resolved - { - // CodeEmbed-Start: mirage1501-resolved - [NetworkMessage] - public struct TelemetryUpdateMessage - { - public int sequenceNumber; - // Correct: Send individual updates frequently instead of a large history array. + public int id; public Vector3 position; - public Quaternion rotation; + // Info Diagnostic: NetworkMessage 'PlayerUpdateMessage' has an estimated serialized size of 13 bytes. } - // CodeEmbed-End: mirage1501-resolved + // CodeEmbed-End: mirage1501-example } } diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1502.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1502.cs deleted file mode 100644 index 896e9b16af9..00000000000 --- a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1502.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Mirage; -using Mirage.Serialization; - -namespace Mirage.Snippets.Analyzers -{ - namespace M1502.Triggering - { - // CodeEmbed-Start: mirage1502-triggering - [NetworkMessage] - public struct ChatMessage - { - // Warning: Unbounded string can be exploited to send megabytes of text - public string text; - } - // CodeEmbed-End: mirage1502-triggering - } - - namespace M1502.Resolved - { - // CodeEmbed-Start: mirage1502-resolved - [NetworkMessage] - public struct ChatMessage - { - // Correct: Bounded string using [BitCount] to limit length - [BitCount(8)] - public string text; - } - // CodeEmbed-End: mirage1502-resolved - } -} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1502.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1502.cs.meta deleted file mode 100644 index 8dc7f8f81bf..00000000000 --- a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1502.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 25016016df9f5a5429a124decf0c2bc9 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1503.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1503.cs deleted file mode 100644 index 574870601a2..00000000000 --- a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1503.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Mirage; -using Mirage.Serialization; - -namespace Mirage.Snippets.Analyzers -{ - namespace M1503.Triggering - { - // CodeEmbed-Start: mirage1503-triggering - public class Player : NetworkBehaviour - { - // Warning: 'Health' uses uncompressed int which has high bit-overhead. - [SyncVar] - public int Health { get; set; } - - // Warning: 'PlayerScale' uses uncompressed float which has high bit-overhead. - [SyncVar] - public float PlayerScale { get; set; } - } - // CodeEmbed-End: mirage1503-triggering - } - - namespace M1503.Resolved - { - // CodeEmbed-Start: mirage1503-resolved - public class Player : NetworkBehaviour - { - // Correct: Restrict Health to 7 bits (0-127 range) - [SyncVar, BitCount(7)] - public int Health { get; set; } - - // Correct: Compress float with a defined range and precision - [SyncVar, FloatPack(10f, 0.01f)] - public float PlayerScale { get; set; } - } - // CodeEmbed-End: mirage1503-resolved - } -} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1503.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1503.cs.meta deleted file mode 100644 index 2e49e1f1653..00000000000 --- a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1503.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 10fad916023430041a6bc426fd773565 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/MtuSizeEstimationTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/MtuSizeEstimationTests.cs index 076305283f1..1af1e585de3 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/MtuSizeEstimationTests.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/MtuSizeEstimationTests.cs @@ -7,35 +7,44 @@ namespace Mirage.Analyzers.Tests public class MtuSizeEstimationTests { [Test] - public async Task Positive_SmallMessageDoesNotTriggerWarning() + public async Task SmallMessageTriggersInfo() { var code = VerifyCS.LoadTestData("MtuSizeEstimation/Positive_SmallMessageDoesNotTriggerWarning.cs"); - await VerifyCS.VerifyAnalyzerAsync(code); + var expected = VerifyCS.Diagnostic("MIRAGE1501") + .WithLocation(0) + .WithArguments("SmallMessage", "13"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); } [Test] - public async Task Positive_LargeMessageReducedByPacking() + public async Task LargeMessageReducedByPackingTriggersInfo() { var code = VerifyCS.LoadTestData("MtuSizeEstimation/Positive_LargeMessageReducedByPacking.cs"); - await VerifyCS.VerifyAnalyzerAsync(code); + var expected = VerifyCS.Diagnostic("MIRAGE1501") + .WithLocation(0) + .WithArguments("PackedTestMessage", "1"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); } [Test] - public async Task Negative_LargeMessageExceedsMtu() + public async Task LargeMessageTriggersInfo() { var code = VerifyCS.LoadTestData("MtuSizeEstimation/Negative_LargeMessageExceedsMtu.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1501") .WithLocation(0) - .WithArguments("HugeMessage", "10240", "1200"); + .WithArguments("HugeMessage", "0"); await VerifyCS.VerifyAnalyzerAsync(code, expected); } [Test] - public async Task Edge_RecursiveStructOrClassRef() + public async Task RecursiveStructOrClassRefTriggersInfo() { var code = VerifyCS.LoadTestData("MtuSizeEstimation/Edge_RecursiveStructOrClassRef.cs"); - await VerifyCS.VerifyAnalyzerAsync(code); + var expected = VerifyCS.Diagnostic("MIRAGE1501") + .WithLocation(0) + .WithArguments("RecursiveMessage", "1"); + await VerifyCS.VerifyAnalyzerAsync(code, expected); } } } diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Edge_RecursiveStructOrClassRef.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Edge_RecursiveStructOrClassRef.cs index 68669648905..8366e3e7f82 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Edge_RecursiveStructOrClassRef.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Edge_RecursiveStructOrClassRef.cs @@ -1,7 +1,7 @@ using Mirage; [NetworkMessage] -public class RecursiveMessage +public class {|#0:RecursiveMessage|} { public RecursiveMessage Parent; public int Value; diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Positive_LargeMessageReducedByPacking.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Positive_LargeMessageReducedByPacking.cs index d1d5e990c85..b31f3d93d01 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Positive_LargeMessageReducedByPacking.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Positive_LargeMessageReducedByPacking.cs @@ -2,7 +2,7 @@ using Mirage.Serialization; [NetworkMessage] -public struct PackedTestMessage +public struct {|#0:PackedTestMessage|} { [FloatPack(0.0f, 1)] // Should estimate size based on 1 bit instead of 4/8 bytes public float CompressedVal; diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Positive_SmallMessageDoesNotTriggerWarning.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Positive_SmallMessageDoesNotTriggerWarning.cs index 9d5adb2bde2..27973c7aea3 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Positive_SmallMessageDoesNotTriggerWarning.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/MtuSizeEstimation/Positive_SmallMessageDoesNotTriggerWarning.cs @@ -2,7 +2,7 @@ using UnityEngine; [NetworkMessage] -public struct SmallMessage +public struct {|#0:SmallMessage|} { public int id; public Vector3 position; diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Negative_UnboundedFieldAndPropertyInMessage.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Negative_UnboundedFieldAndPropertyInMessage.cs deleted file mode 100644 index df34cbfdb59..00000000000 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Negative_UnboundedFieldAndPropertyInMessage.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Mirage; -using System.Collections.Generic; - -[NetworkMessage] -public struct InvalidMessage -{ - public string {|#0:Name|}; - - public int[] {|#1:Scores|}; - - public List {|#2:Positions|} { get; set; } -} - -namespace Mirage -{ - public class NetworkMessageAttribute : System.Attribute {} - public class NetworkBehaviour {} - public class ServerRpcAttribute : System.Attribute {} - public class ClientRpcAttribute : System.Attribute {} -} -namespace Mirage.Serialization -{ - public class BitCountAttribute : System.Attribute - { - public BitCountAttribute(int bits) {} - } - public class VarIntAttribute : System.Attribute {} - public class BitCountFromRangeAttribute : System.Attribute {} - public class VarIntBlocksAttribute : System.Attribute {} -} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Negative_UnboundedParameterInRpc.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Negative_UnboundedParameterInRpc.cs deleted file mode 100644 index e523c9a6f2c..00000000000 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Negative_UnboundedParameterInRpc.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Mirage; -using System.Collections.Generic; - -public class PlayerBehaviour : NetworkBehaviour -{ - [ServerRpc] - public void CmdSendText(string {|#0:text|}) {} - - [ClientRpc] - public void RpcUpdateList(List {|#1:items|}) {} -} - -namespace Mirage -{ - public class NetworkMessageAttribute : System.Attribute {} - public class NetworkBehaviour {} - public class ServerRpcAttribute : System.Attribute {} - public class ClientRpcAttribute : System.Attribute {} -} -namespace Mirage.Serialization -{ - public class BitCountAttribute : System.Attribute - { - public BitCountAttribute(int bits) {} - } - public class VarIntAttribute : System.Attribute {} - public class BitCountFromRangeAttribute : System.Attribute {} - public class VarIntBlocksAttribute : System.Attribute {} -} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Positive_BoundedStringAndCollection.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Positive_BoundedStringAndCollection.cs deleted file mode 100644 index f8709e2cf34..00000000000 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Positive_BoundedStringAndCollection.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Mirage; -using Mirage.Serialization; -using System.Collections.Generic; - -[NetworkMessage] -public struct ValidMessage -{ - [BitCount(8)] - public string Name; - - [VarInt] - public int[] Scores; - - [BitCount(10)] - public List Positions; -} - -namespace Mirage -{ - public class NetworkMessageAttribute : System.Attribute {} - public class NetworkBehaviour {} - public class ServerRpcAttribute : System.Attribute {} - public class ClientRpcAttribute : System.Attribute {} -} -namespace Mirage.Serialization -{ - public class BitCountAttribute : System.Attribute - { - public BitCountAttribute(int bits) {} - } - public class VarIntAttribute : System.Attribute {} - public class BitCountFromRangeAttribute : System.Attribute {} - public class VarIntBlocksAttribute : System.Attribute {} -} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Positive_NonNetworkContext.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Positive_NonNetworkContext.cs deleted file mode 100644 index 7609fcca31d..00000000000 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UnboundedCollection/Positive_NonNetworkContext.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections.Generic; - -public class StandardClass -{ - public string UnboundedString; - public List UnboundedList; - - public void NormalMethod(string param) {} -} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Negative_UncompressedFieldsInMessage.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Negative_UncompressedFieldsInMessage.cs deleted file mode 100644 index 634f8a94dc1..00000000000 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Negative_UncompressedFieldsInMessage.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Mirage; -using UnityEngine; - -[NetworkMessage] -public struct StatusMessage -{ - public double {|#0:timestamp|}; - public Vector2 {|#1:offset|}; -} - -namespace Mirage -{ - public class NetworkMessageAttribute : System.Attribute {} - public class NetworkBehaviour {} - public class SyncVarAttribute : System.Attribute {} - public class ServerRpcAttribute : System.Attribute {} - public class ClientRpcAttribute : System.Attribute {} -} -namespace Mirage.Serialization -{ - public class BitCountAttribute : System.Attribute - { - public BitCountAttribute(int bits) {} - } - public class BitCountFromRangeAttribute : System.Attribute {} - public class VarIntAttribute : System.Attribute {} - public class VarIntBlocksAttribute : System.Attribute {} - public class FloatPackAttribute : System.Attribute {} - public class Vector2PackAttribute : System.Attribute {} - public class Vector3PackAttribute : System.Attribute {} - public class QuaternionPackAttribute : System.Attribute {} -} -namespace UnityEngine -{ - public struct Vector2 - { - public float x; - public float y; - } - public struct Vector3 - { - public float x; - public float y; - public float z; - } - public struct Quaternion - { - public float x; - public float y; - public float z; - public float w; - } -} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Negative_UncompressedRpcParameters.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Negative_UncompressedRpcParameters.cs deleted file mode 100644 index 5cc21fcd0fe..00000000000 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Negative_UncompressedRpcParameters.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Mirage; -using UnityEngine; - -public class Player : NetworkBehaviour -{ - [ServerRpc] - public void CmdUpdateStatus(int {|#0:score|}, float {|#1:val|}) {} - - [ClientRpc] - public void RpcUpdatePhysics(Vector3 {|#2:pos|}, Quaternion {|#3:rot|}) {} -} - -namespace Mirage -{ - public class NetworkMessageAttribute : System.Attribute {} - public class NetworkBehaviour {} - public class SyncVarAttribute : System.Attribute {} - public class ServerRpcAttribute : System.Attribute {} - public class ClientRpcAttribute : System.Attribute {} -} -namespace Mirage.Serialization -{ - public class BitCountAttribute : System.Attribute - { - public BitCountAttribute(int bits) {} - } - public class BitCountFromRangeAttribute : System.Attribute {} - public class VarIntAttribute : System.Attribute {} - public class VarIntBlocksAttribute : System.Attribute {} - public class FloatPackAttribute : System.Attribute {} - public class Vector2PackAttribute : System.Attribute {} - public class Vector3PackAttribute : System.Attribute {} - public class QuaternionPackAttribute : System.Attribute {} -} -namespace UnityEngine -{ - public struct Vector2 - { - public float x; - public float y; - } - public struct Vector3 - { - public float x; - public float y; - public float z; - } - public struct Quaternion - { - public float x; - public float y; - public float z; - public float w; - } -} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Negative_UncompressedSyncVars.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Negative_UncompressedSyncVars.cs deleted file mode 100644 index 42dc8ca9ed2..00000000000 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Negative_UncompressedSyncVars.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Mirage; - -public class Player : NetworkBehaviour -{ - [SyncVar] - public int {|#0:Health|} { get; set; } - - [SyncVar] - public float {|#1:PlayerScale|}; -} - -namespace Mirage -{ - public class NetworkMessageAttribute : System.Attribute {} - public class NetworkBehaviour {} - public class SyncVarAttribute : System.Attribute {} - public class ServerRpcAttribute : System.Attribute {} - public class ClientRpcAttribute : System.Attribute {} -} -namespace Mirage.Serialization -{ - public class BitCountAttribute : System.Attribute - { - public BitCountAttribute(int bits) {} - } - public class BitCountFromRangeAttribute : System.Attribute {} - public class VarIntAttribute : System.Attribute {} - public class VarIntBlocksAttribute : System.Attribute {} - public class FloatPackAttribute : System.Attribute {} - public class Vector2PackAttribute : System.Attribute {} - public class Vector3PackAttribute : System.Attribute {} - public class QuaternionPackAttribute : System.Attribute {} -} -namespace UnityEngine -{ - public struct Vector2 - { - public float x; - public float y; - } - public struct Vector3 - { - public float x; - public float y; - public float z; - } - public struct Quaternion - { - public float x; - public float y; - public float z; - public float w; - } -} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Positive_AllowedUncompressedTypes.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Positive_AllowedUncompressedTypes.cs deleted file mode 100644 index f5e91650145..00000000000 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Positive_AllowedUncompressedTypes.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Mirage; - -[NetworkMessage] -public struct AllowedMessage -{ - public bool isReady; - public byte health; - public char category; - public short level; -} - -namespace Mirage -{ - public class NetworkMessageAttribute : System.Attribute {} - public class NetworkBehaviour {} - public class SyncVarAttribute : System.Attribute {} - public class ServerRpcAttribute : System.Attribute {} - public class ClientRpcAttribute : System.Attribute {} -} -namespace Mirage.Serialization -{ - public class BitCountAttribute : System.Attribute - { - public BitCountAttribute(int bits) {} - } - public class BitCountFromRangeAttribute : System.Attribute {} - public class VarIntAttribute : System.Attribute {} - public class VarIntBlocksAttribute : System.Attribute {} - public class FloatPackAttribute : System.Attribute {} - public class Vector2PackAttribute : System.Attribute {} - public class Vector3PackAttribute : System.Attribute {} - public class QuaternionPackAttribute : System.Attribute {} -} -namespace UnityEngine -{ - public struct Vector2 - { - public float x; - public float y; - } - public struct Vector3 - { - public float x; - public float y; - public float z; - } - public struct Quaternion - { - public float x; - public float y; - public float z; - public float w; - } -} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Positive_CompressedPrimitivesAndVectors.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Positive_CompressedPrimitivesAndVectors.cs deleted file mode 100644 index 7a6e4914c59..00000000000 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/TestData/UncompressedPrimitive/Positive_CompressedPrimitivesAndVectors.cs +++ /dev/null @@ -1,66 +0,0 @@ -using Mirage; -using Mirage.Serialization; -using UnityEngine; - -[NetworkMessage] -public struct ValidMessage -{ - [BitCount(8)] - public int score; - - [FloatPack] - public float temperature; - - [Vector2Pack] - public Vector2 velocity; - - [Vector3Pack] - public Vector3 position; - - [QuaternionPack] - public Quaternion rotation; -} - -namespace Mirage -{ - public class NetworkMessageAttribute : System.Attribute {} - public class NetworkBehaviour {} - public class SyncVarAttribute : System.Attribute {} - public class ServerRpcAttribute : System.Attribute {} - public class ClientRpcAttribute : System.Attribute {} -} -namespace Mirage.Serialization -{ - public class BitCountAttribute : System.Attribute - { - public BitCountAttribute(int bits) {} - } - public class BitCountFromRangeAttribute : System.Attribute {} - public class VarIntAttribute : System.Attribute {} - public class VarIntBlocksAttribute : System.Attribute {} - public class FloatPackAttribute : System.Attribute {} - public class Vector2PackAttribute : System.Attribute {} - public class Vector3PackAttribute : System.Attribute {} - public class QuaternionPackAttribute : System.Attribute {} -} -namespace UnityEngine -{ - public struct Vector2 - { - public float x; - public float y; - } - public struct Vector3 - { - public float x; - public float y; - public float z; - } - public struct Quaternion - { - public float x; - public float y; - public float z; - public float w; - } -} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/UnboundedCollectionTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/UnboundedCollectionTests.cs deleted file mode 100644 index 3bce17e762a..00000000000 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/UnboundedCollectionTests.cs +++ /dev/null @@ -1,44 +0,0 @@ -using NUnit.Framework; -using System.Threading.Tasks; - -namespace Mirage.Analyzers.Tests -{ - [TestFixture] - public class UnboundedCollectionTests - { - [Test] - public async Task Positive_BoundedStringAndCollection() - { - var code = VerifyCS.LoadTestData("UnboundedCollection/Positive_BoundedStringAndCollection.cs"); - await VerifyCS.VerifyAnalyzerAsync(code); - } - - [Test] - public async Task Positive_NonNetworkContext() - { - var code = VerifyCS.LoadTestData("UnboundedCollection/Positive_NonNetworkContext.cs"); - await VerifyCS.VerifyAnalyzerAsync(code); - } - - [Test] - public async Task Negative_UnboundedFieldAndPropertyInMessage() - { - var code = VerifyCS.LoadTestData("UnboundedCollection/Negative_UnboundedFieldAndPropertyInMessage.cs"); - var expectedField1 = VerifyCS.Diagnostic("MIRAGE1502").WithLocation(0).WithArguments("Name", "String"); - var expectedField2 = VerifyCS.Diagnostic("MIRAGE1502").WithLocation(1).WithArguments("Scores", "Int32[]"); - var expectedProp = VerifyCS.Diagnostic("MIRAGE1502").WithLocation(2).WithArguments("Positions", "List"); - - await VerifyCS.VerifyAnalyzerAsync(code, expectedField1, expectedField2, expectedProp); - } - - [Test] - public async Task Negative_UnboundedParameterInRpc() - { - var code = VerifyCS.LoadTestData("UnboundedCollection/Negative_UnboundedParameterInRpc.cs"); - var expectedParam1 = VerifyCS.Diagnostic("MIRAGE1502").WithLocation(0).WithArguments("text", "String"); - var expectedParam2 = VerifyCS.Diagnostic("MIRAGE1502").WithLocation(1).WithArguments("items", "List"); - - await VerifyCS.VerifyAnalyzerAsync(code, expectedParam1, expectedParam2); - } - } -} diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/UncompressedPrimitiveTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/UncompressedPrimitiveTests.cs deleted file mode 100644 index 7fe4ed2f6fc..00000000000 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/UncompressedPrimitiveTests.cs +++ /dev/null @@ -1,55 +0,0 @@ -using NUnit.Framework; -using System.Threading.Tasks; - -namespace Mirage.Analyzers.Tests -{ - [TestFixture] - public class UncompressedPrimitiveTests - { - [Test] - public async Task Positive_CompressedPrimitivesAndVectors() - { - var code = VerifyCS.LoadTestData("UncompressedPrimitive/Positive_CompressedPrimitivesAndVectors.cs"); - await VerifyCS.VerifyAnalyzerAsync(code); - } - - [Test] - public async Task Positive_AllowedUncompressedTypes() - { - var code = VerifyCS.LoadTestData("UncompressedPrimitive/Positive_AllowedUncompressedTypes.cs"); - await VerifyCS.VerifyAnalyzerAsync(code); - } - - [Test] - public async Task Negative_UncompressedSyncVars() - { - var code = VerifyCS.LoadTestData("UncompressedPrimitive/Negative_UncompressedSyncVars.cs"); - var expectedProp = VerifyCS.Diagnostic("MIRAGE1503").WithLocation(0).WithArguments("Health", "Int32"); - var expectedField = VerifyCS.Diagnostic("MIRAGE1503").WithLocation(1).WithArguments("PlayerScale", "Single"); - - await VerifyCS.VerifyAnalyzerAsync(code, expectedProp, expectedField); - } - - [Test] - public async Task Negative_UncompressedRpcParameters() - { - var code = VerifyCS.LoadTestData("UncompressedPrimitive/Negative_UncompressedRpcParameters.cs"); - var expectedScore = VerifyCS.Diagnostic("MIRAGE1503").WithLocation(0).WithArguments("score", "Int32"); - var expectedVal = VerifyCS.Diagnostic("MIRAGE1503").WithLocation(1).WithArguments("val", "Single"); - var expectedPos = VerifyCS.Diagnostic("MIRAGE1503").WithLocation(2).WithArguments("pos", "Vector3"); - var expectedRot = VerifyCS.Diagnostic("MIRAGE1503").WithLocation(3).WithArguments("rot", "Quaternion"); - - await VerifyCS.VerifyAnalyzerAsync(code, expectedScore, expectedVal, expectedPos, expectedRot); - } - - [Test] - public async Task Negative_UncompressedFieldsInMessage() - { - var code = VerifyCS.LoadTestData("UncompressedPrimitive/Negative_UncompressedFieldsInMessage.cs"); - var expectedTimestamp = VerifyCS.Diagnostic("MIRAGE1503").WithLocation(0).WithArguments("timestamp", "Double"); - var expectedOffset = VerifyCS.Diagnostic("MIRAGE1503").WithLocation(1).WithArguments("offset", "Vector2"); - - await VerifyCS.VerifyAnalyzerAsync(code, expectedTimestamp, expectedOffset); - } - } -} diff --git a/CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.Performance.cs b/CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.Performance.cs index 37971722b44..bd2d7052e03 100644 --- a/CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.Performance.cs +++ b/CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.Performance.cs @@ -7,61 +7,6 @@ namespace Mirage.Analyzers { public partial class MirageAnalyzer { - private static void AnalyzeFieldPerformance(SymbolAnalysisContext context) - { - var fieldSymbol = (IFieldSymbol)context.Symbol; - - if (fieldSymbol.ContainingType != null && MirageAttributes.NetworkMessage.Has(fieldSymbol.ContainingType)) - { - if (IsUnboundedCollectionOrString(fieldSymbol, fieldSymbol.Type)) - { - var diagnostic = Diagnostic.Create(MirageRules.PerformanceUnboundedCollectionRule, fieldSymbol.Locations[0], fieldSymbol.Name, fieldSymbol.Type.Name); - context.ReportDiagnostic(diagnostic); - } - - CheckHighOverhead(context, fieldSymbol, fieldSymbol.Type); - } - - if (MirageAttributes.SyncVar.Has(fieldSymbol)) - CheckHighOverhead(context, fieldSymbol, fieldSymbol.Type); - } - - private static void AnalyzePropertyPerformance(SymbolAnalysisContext context) - { - var propertySymbol = (IPropertySymbol)context.Symbol; - - if (propertySymbol.ContainingType != null && MirageAttributes.NetworkMessage.Has(propertySymbol.ContainingType)) - { - if (IsUnboundedCollectionOrString(propertySymbol, propertySymbol.Type)) - { - var diagnostic = Diagnostic.Create(MirageRules.PerformanceUnboundedCollectionRule, propertySymbol.Locations[0], propertySymbol.Name, propertySymbol.Type.Name); - context.ReportDiagnostic(diagnostic); - } - - CheckHighOverhead(context, propertySymbol, propertySymbol.Type); - } - - if (MirageAttributes.SyncVar.Has(propertySymbol)) - CheckHighOverhead(context, propertySymbol, propertySymbol.Type); - } - - private static void AnalyzeParameterPerformance(SymbolAnalysisContext context) - { - var parameterSymbol = (IParameterSymbol)context.Symbol; - var containingMethod = parameterSymbol.ContainingSymbol as IMethodSymbol; - - if (containingMethod != null && IsRpcMethod(containingMethod)) - { - if (IsUnboundedCollectionOrString(parameterSymbol, parameterSymbol.Type)) - { - var diagnostic = Diagnostic.Create(MirageRules.PerformanceUnboundedCollectionRule, parameterSymbol.Locations[0], parameterSymbol.Name, parameterSymbol.Type.Name); - context.ReportDiagnostic(diagnostic); - } - - CheckHighOverhead(context, parameterSymbol, parameterSymbol.Type); - } - } - private static void AnalyzeNamedTypePerformance(SymbolAnalysisContext context) { var typeSymbol = (INamedTypeSymbol)context.Symbol; @@ -70,44 +15,7 @@ private static void AnalyzeNamedTypePerformance(SymbolAnalysisContext context) { var visited = new HashSet(SymbolEqualityComparer.Default); int estimatedSize = EstimateSerializedSize(typeSymbol, visited); - if (estimatedSize > 1200) - { - var diagnostic = Diagnostic.Create(MirageRules.PerformanceMtuExceededRule, typeSymbol.Locations[0], typeSymbol.Name, estimatedSize, 1200); - context.ReportDiagnostic(diagnostic); - } - } - } - - private static bool IsUnboundedCollectionOrString(ISymbol symbol, ITypeSymbol type) - { - bool isString = type.SpecialType == SpecialType.System_String; - bool isCollection = type.TypeKind == TypeKind.Array || - (type is INamedTypeSymbol namedType && MirageTypes.IEnumerable.Implements(namedType)); - - if (!isString && !isCollection) - return false; - - if (MirageAttributes.HasCompressionAttribute(symbol, type)) - return false; - - return true; - } - - private static void CheckHighOverhead(SymbolAnalysisContext context, ISymbol symbol, ITypeSymbol type) - { - bool isOverheadType = type.SpecialType == SpecialType.System_Int32 || - type.SpecialType == SpecialType.System_UInt32 || - type.SpecialType == SpecialType.System_Int64 || - type.SpecialType == SpecialType.System_UInt64 || - type.SpecialType == SpecialType.System_Single || - type.SpecialType == SpecialType.System_Double || - MirageTypes.Vector2.Is(type) || - MirageTypes.Vector3.Is(type) || - MirageTypes.Quaternion.Is(type); - - if (isOverheadType && !MirageAttributes.HasCompressionAttribute(symbol, type)) - { - var diagnostic = Diagnostic.Create(MirageRules.PerformanceHighOverheadRule, symbol.Locations[0], symbol.Name, type.Name); + var diagnostic = Diagnostic.Create(MirageRules.PerformanceMessageSizeRule, typeSymbol.Locations[0], typeSymbol.Name, estimatedSize); context.ReportDiagnostic(diagnostic); } } @@ -151,7 +59,7 @@ private static int EstimateSerializedSize(ITypeSymbol type, HashSet case SpecialType.System_Decimal: return 16; case SpecialType.System_String: - return 32; + return 0; // Dynamic type: skip/treat as variable } if (type.ContainingNamespace?.ToDisplayString() == "UnityEngine") @@ -176,15 +84,13 @@ private static int EstimateSerializedSize(ITypeSymbol type, HashSet } if (type is IArrayTypeSymbol arrayType) - return 128 * EstimateSerializedSize(arrayType.ElementType, visited); + return 0; // Dynamic type: skip/treat as variable if (type is INamedTypeSymbol namedType && namedType.IsGenericType) { if (MirageTypes.IEnumerable.Implements(namedType)) { - var elemType = namedType.TypeArguments.Length > 0 ? namedType.TypeArguments[0] : null; - if (elemType != null) - return 128 * EstimateSerializedSize(elemType, visited); + return 0; // Dynamic type: skip/treat as variable } } diff --git a/CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.cs b/CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.cs index 7edc1002b8f..fb2262482d9 100644 --- a/CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.cs +++ b/CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.cs @@ -63,14 +63,12 @@ private static void AnalyzeField(SymbolAnalysisContext context, CustomSerializer { AnalyzeFieldSyncVars(context); AnalyzeFieldSerialization(context, serializers); - AnalyzeFieldPerformance(context); } private static void AnalyzeProperty(SymbolAnalysisContext context, CustomSerializers serializers) { AnalyzePropertySyncVars(context); AnalyzePropertySerialization(context, serializers); - AnalyzePropertyPerformance(context); } private static void AnalyzeMethod(SymbolAnalysisContext context, CustomSerializers serializers) @@ -83,7 +81,6 @@ private static void AnalyzeParameter(SymbolAnalysisContext context, CustomSerial { AnalyzeParameterRpcs(context); AnalyzeParameterSerialization(context, serializers); - AnalyzeParameterPerformance(context); } private static void AnalyzeNamedType(SymbolAnalysisContext context) diff --git a/CSharpAnalyzers/Mirage.Analyzers/MirageRules.cs b/CSharpAnalyzers/Mirage.Analyzers/MirageRules.cs index 67247d68eef..c65d386394f 100644 --- a/CSharpAnalyzers/Mirage.Analyzers/MirageRules.cs +++ b/CSharpAnalyzers/Mirage.Analyzers/MirageRules.cs @@ -21,9 +21,7 @@ public static class MirageRules public const string MismatchedSerializationDiagnosticId = "MIRAGE1303"; public const string LifecycleNetworkStateDiagnosticId = "MIRAGE1401"; public const string LifecycleMissingBaseCallDiagnosticId = "MIRAGE1402"; - public const string PerformanceMtuExceededDiagnosticId = "MIRAGE1501"; - public const string PerformanceUnboundedCollectionDiagnosticId = "MIRAGE1502"; - public const string PerformanceHighOverheadDiagnosticId = "MIRAGE1503"; + public const string PerformanceMessageSizeDiagnosticId = "MIRAGE1501"; public static readonly DiagnosticDescriptor SyncVarRule = new DiagnosticDescriptor( SyncVarDiagnosticId, @@ -185,36 +183,16 @@ public static class MirageRules description: "Overriding OnSerialize or OnDeserialize in a class that inherits from a class with synchronized state must call the base method.", helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1402"); - public static readonly DiagnosticDescriptor PerformanceMtuExceededRule = new DiagnosticDescriptor( - PerformanceMtuExceededDiagnosticId, - "Network Message Exceeds Safe MTU", - "NetworkMessage '{0}' has an estimated serialized size of {1} bytes, which exceeds the safe MTU of {2} bytes", + public static readonly DiagnosticDescriptor PerformanceMessageSizeRule = new DiagnosticDescriptor( + PerformanceMessageSizeDiagnosticId, + "Network Message Serialized Size Estimation", + "NetworkMessage '{0}' has an estimated serialized size of {1} bytes.", "Performance", - DiagnosticSeverity.Warning, + DiagnosticSeverity.Info, isEnabledByDefault: true, - description: "Network messages should remain within the safe MTU to avoid fragmentation.", + description: "Estimated serialized size of NetworkMessage.", helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1501"); - public static readonly DiagnosticDescriptor PerformanceUnboundedCollectionRule = new DiagnosticDescriptor( - PerformanceUnboundedCollectionDiagnosticId, - "Unbounded String or Collection", - "Field/property/parameter '{0}' of type '{1}' is unbounded. Restrict its size using [BitCount] or another packing attribute.", - "Performance", - DiagnosticSeverity.Warning, - isEnabledByDefault: true, - description: "Unbounded strings or collections can be exploited to cause memory exhaustion.", - helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1502"); - - public static readonly DiagnosticDescriptor PerformanceHighOverheadRule = new DiagnosticDescriptor( - PerformanceHighOverheadDiagnosticId, - "High Bit-Overhead Primitive Type", - "Field/property/parameter '{0}' of type '{1}' is uncompressed. Consider applying a bit-packing or compression attribute.", - "Performance", - DiagnosticSeverity.Warning, - isEnabledByDefault: true, - description: "Uncompressed primitives or vectors consume unnecessary bandwidth.", - helpLinkUri: "https://miragenet.github.io/Mirage/docs/analyzers/MIRAGE1503"); - public static readonly ImmutableArray SupportedDiagnostics = ImmutableArray.Create( SyncVarRule, AutoPropertyRule, @@ -232,9 +210,7 @@ public static class MirageRules MismatchedSerializationRule, LifecycleNetworkStateRule, LifecycleMissingBaseCallRule, - PerformanceMtuExceededRule, - PerformanceUnboundedCollectionRule, - PerformanceHighOverheadRule + PerformanceMessageSizeRule ); } } diff --git a/CSharpAnalyzers/analyzers_roadmap.md b/CSharpAnalyzers/analyzers_roadmap.md index 2c693c2059f..0042cf45116 100644 --- a/CSharpAnalyzers/analyzers_roadmap.md +++ b/CSharpAnalyzers/analyzers_roadmap.md @@ -57,11 +57,9 @@ IMPORTANT: if boxes arn't checked here, assume existing files are out-of-date an --- ## Group 6: Network Performance & Size Estimation (`MIRAGE1500 – MIRAGE1599`) -*Note: These rules are designated as `DiagnosticSeverity.Warning` to warn about security/performance issues, and output a parser-friendly format (e.g. JSON-like summary of size calculations) to facilitate future CodeLens or editor tooling integration.* +*Note: These rules are designated as `DiagnosticSeverity.Info` to provide size estimation feedback for messages.* -* [x][x][x] **`MIRAGE1501`**: Network Message Exceeds Safe MTU (Warning if estimated serialization size exceeds 1200 bytes). -* [x][x][x] **`MIRAGE1502`**: Unbounded String or Collection (Warning if string/collection has no defined size bounds). -* [x][x][x] **`MIRAGE1503`**: High Bit-Over-Head Primitive Type (Warning on uncompressed float/vector transfers). +* [x][x][x] **`MIRAGE1501`**: Network Message Serialized Size Estimation (Info diagnostic reporting the estimated serialization size of all NetworkMessage structs/classes). --- diff --git a/doc/docs/analyzers/MIRAGE1501.md b/doc/docs/analyzers/MIRAGE1501.md index 5d8bcc95417..03801265cc4 100644 --- a/doc/docs/analyzers/MIRAGE1501.md +++ b/doc/docs/analyzers/MIRAGE1501.md @@ -1,19 +1,16 @@ -# MIRAGE1501: Network Message Exceeds Safe MTU +# MIRAGE1501: Network Message Serialized Size Estimation -## The Problem -A `[NetworkMessage]` struct/class has a static or maximum serialized size that exceeds the safe Maximum Transmission Unit (MTU) of the transport layer (typically 1200 - 1400 bytes). - -If a single message size exceeds the MTU, it must be fragmented at the transport or IP layer. IP fragmentation increases packet loss rates, latency, and connection instability. Designing messages that stay within the safe MTU boundary improves network reliability and performance. +## Description +This diagnostic runs on all `[NetworkMessage]` types to report their estimated serialized size. This helps developers analyze and optimize the bandwidth footprint of their network messages directly in the editor. --- -## Example of Triggering Code -{{{ Path:'Snippets/Analyzers/Mirage1501.cs' Name:'mirage1501-triggering' }}} +## Example +{{{ Path:'Snippets/Analyzers/Mirage1501.cs' Name:'mirage1501-example' }}} --- -## How to Resolve - -Break large messages down into smaller chunks, use compression, or send raw bulk data using a streaming/chunking API instead of a single massive NetworkMessage. - -{{{ Path:'Snippets/Analyzers/Mirage1501.cs' Name:'mirage1501-resolved' }}} +## Size Estimation Details +- Primitives (int, float, double, etc.) are estimated based on their standard bit-packing or serialization footprint. +- Unity structs like `Vector3` and `Quaternion` are evaluated at their full uncompressed precision unless decorated with packing attributes. +- Dynamic types (strings, arrays, lists) are treated as variable and skipped in the static size calculation (evaluated as `0` bytes). diff --git a/doc/docs/analyzers/MIRAGE1502.md b/doc/docs/analyzers/MIRAGE1502.md deleted file mode 100644 index 6629a042bbe..00000000000 --- a/doc/docs/analyzers/MIRAGE1502.md +++ /dev/null @@ -1,19 +0,0 @@ -# MIRAGE1502: Unbounded String or Collection - -## The Problem -A network message field or RPC parameter contains a `string`, `List`, `T[]` array, or other collection without specifying a maximum size/length limit. - -Allowing unbounded strings or collections in network messages introduces security risks (such as memory exhaustion attacks, out-of-memory crashes, or denial of service) if a client sends an extremely large payload. - ---- - -## Example of Triggering Code -{{{ Path:'Snippets/Analyzers/Mirage1502.cs' Name:'mirage1502-triggering' }}} - ---- - -## How to Resolve - -Use size-limiting attributes (such as `[BitCount]` or other string/collection size limiters) to restrict the collection size at serialization time, or enforce maximum limits during deserialization (such as setting `MaxDeltaCount` or `MaxElements` on SyncObjects). - -{{{ Path:'Snippets/Analyzers/Mirage1502.cs' Name:'mirage1502-resolved' }}} diff --git a/doc/docs/analyzers/MIRAGE1503.md b/doc/docs/analyzers/MIRAGE1503.md deleted file mode 100644 index 8bc8e245584..00000000000 --- a/doc/docs/analyzers/MIRAGE1503.md +++ /dev/null @@ -1,19 +0,0 @@ -# MIRAGE1503: High Bit-Overhead Primitive Type - -## The Problem -A primitive type (like `int`, `uint`, `long`, `ulong`, `float`, or `double`) is used in a `[SyncVar]`, RPC parameter, or `[NetworkMessage]` field without any bit-packing, compression, or range-limiting attributes. - -Standard uncompressed primitives write their full bit-width (e.g. 32 bits for `int`/`float`, 64 bits for `long`/`double`) onto the network buffer, even if the runtime values are small or do not require that level of precision. Over time, this leads to unnecessary bandwidth consumption. - ---- - -## Example of Triggering Code -{{{ Path:'Snippets/Analyzers/Mirage1503.cs' Name:'mirage1503-triggering' }}} - ---- - -## How to Resolve - -Decorate the fields/properties with appropriate compression attributes like `[BitCount]`, `[VarInt]`, `[FloatPack]`, or `[BitCountFromRange]` to minimize the serialized bit size. - -{{{ Path:'Snippets/Analyzers/Mirage1503.cs' Name:'mirage1503-resolved' }}} diff --git a/doc/docs/analyzers/index.md b/doc/docs/analyzers/index.md index f3c3bef721b..4e1f0d78216 100644 --- a/doc/docs/analyzers/index.md +++ b/doc/docs/analyzers/index.md @@ -29,9 +29,7 @@ Mirage uses Roslyn Analyzers to provide compile-time validation for network code | [MIRAGE1305](MIRAGE1305.md) | Missing NetworkMessage Attribute | Warning | Warns if a type is sent or registered as a message, but lacks the `[NetworkMessage]` attribute. | | [MIRAGE1401](MIRAGE1401.md) | Accessing Network State in Awake/Start | Warning | Warns against accessing network states like `IsServer` during early Unity lifecycle phases. | | [MIRAGE1402](MIRAGE1402.md) | Missing base Call in OnSerialize/OnDeserialize | Warning | Ensures overriding `OnSerialize` or `OnDeserialize` in derived classes calls the base implementation. | -| [MIRAGE1501](MIRAGE1501.md) | Network Message Exceeds Safe MTU | Warning | Warns when a message exceeds the safe Maximum Transmission Unit (MTU) to prevent IP fragmentation. | -| [MIRAGE1502](MIRAGE1502.md) | Unbounded String or Collection | Warning | Warns about unbounded strings/collections in network messages that could trigger memory exploitation. | -| [MIRAGE1503](MIRAGE1503.md) | High Bit-Overhead Primitive Type | Warning | Recommends bit-packing/compression attributes on primitive types to optimize network bandwidth. | +| [MIRAGE1501](MIRAGE1501.md) | Network Message Serialized Size Estimation | Info | Estimates the serialized size of all `[NetworkMessage]` types to help analyze bandwidth usage. | --- @@ -120,13 +118,7 @@ Derived classes overriding custom `OnSerialize` or `OnDeserialize` methods must --- -### Group 6: Performance & Size Estimation +### Group 6: Performance & Size Estimation (`MIRAGE1500 – MIRAGE1599`) -#### [MIRAGE1501: Network Message Exceeds Safe MTU](MIRAGE1501.md) -If a network message's maximum serialized size exceeds the safe Maximum Transmission Unit (MTU) threshold (typically 1200 - 1400 bytes), this warning is triggered. Large packets require IP fragmentation, which dramatically increases network packet loss. To resolve, compress data fields or split large payloads across multiple chunk messages. - -#### [MIRAGE1502: Unbounded String or Collection](MIRAGE1502.md) -Declaring string or collection fields in network messages without a size limit introduces security risks, allowing malicious clients to cause memory exhaustion on the server. To resolve, use validation attributes like `[BitCount]` to limit serialization size, or define max sizes on collections. - -#### [MIRAGE1503: High Bit-Overhead Primitive Type](MIRAGE1503.md) -Using uncompressed primitive types (such as standard `int`, `long`, or `float`) inside `[SyncVar]` properties or network message fields consumes unnecessary bandwidth. This warning suggests using compression attributes to minimize serialized bit sizes. To resolve this, apply attributes such as `[BitCount]`, `[VarInt]`, or `[FloatPack]`. +#### [MIRAGE1501: Network Message Serialized Size Estimation](MIRAGE1501.md) +Estimates the serialized size of all `[NetworkMessage]` structs/classes and outputs it as an Info diagnostic to help track and optimize bandwidth usage. Dynamic types like strings, arrays, and lists are treated as variable (skipped in size calculation). From 624db6adf3c2e2e0d673ab674773df57f541c512 Mon Sep 17 00:00:00 2001 From: James Frowen Date: Tue, 2 Jun 2026 15:10:33 +0100 Subject: [PATCH 35/41] fixing snippet compile errors --- .../RemoteActions/ClientRpcExamples.cs | 7 ++- .../RpcExampleChangeNameGenerated.cs | 43 ++++++------------- 2 files changed, 15 insertions(+), 35 deletions(-) diff --git a/Assets/Mirage/Samples~/Snippets/RemoteActions/ClientRpcExamples.cs b/Assets/Mirage/Samples~/Snippets/RemoteActions/ClientRpcExamples.cs index 1f8005795e8..a8de1767e6c 100644 --- a/Assets/Mirage/Samples~/Snippets/RemoteActions/ClientRpcExamples.cs +++ b/Assets/Mirage/Samples~/Snippets/RemoteActions/ClientRpcExamples.cs @@ -1,5 +1,4 @@ using UnityEngine; -using Mirage; namespace Mirage.Snippets.RemoteActions.ClientRpcSimple { @@ -7,7 +6,7 @@ namespace Mirage.Snippets.RemoteActions.ClientRpcSimple public class MyClientRpcExampleBehaviour : NetworkBehaviour { [ClientRpc] - public void MyRpcFunction() + public void MyRpcFunction() { // Code to invoke on client } @@ -21,7 +20,7 @@ namespace Mirage.Snippets.RemoteActions.ClientRpcPlayer public class MyClientRpcExampleBehaviour : NetworkBehaviour { [ClientRpc(target = RpcTarget.Player)] - public void MyRpcFunction(NetworkPlayer target) + public void MyRpcFunction(INetworkPlayer target) { // Code to invoke on client } @@ -66,7 +65,7 @@ private void Magic(GameObject target, int damage) { target.GetComponent().health -= damage; - NetworkIdentity opponentIdentity = target.GetComponent(); + var opponentIdentity = target.GetComponent(); DoMagic(opponentIdentity.Owner, damage); } diff --git a/Assets/Mirage/Samples~/Snippets/RemoteActions/RpcExampleChangeNameGenerated.cs b/Assets/Mirage/Samples~/Snippets/RemoteActions/RpcExampleChangeNameGenerated.cs index 3502d4ec33d..e57e62fe94b 100644 --- a/Assets/Mirage/Samples~/Snippets/RemoteActions/RpcExampleChangeNameGenerated.cs +++ b/Assets/Mirage/Samples~/Snippets/RemoteActions/RpcExampleChangeNameGenerated.cs @@ -1,31 +1,8 @@ -using System; -using Mirage; -using Mirage.Serialization; using Mirage.RemoteCalls; +using Mirage.Serialization; namespace Mirage.Snippets.RemoteActions.RpcExamplesGenerated { - using NetworkBehaviour = DummyNetworkBehaviour; - // Dummy classes/aliases to make the snippet code compile in Unity - public class DummyNetworkBehaviour - { - public bool IsServer { get; set; } - public DummyRemoteCallCollection remoteCallCollection { get; set; } = new DummyRemoteCallCollection(); - protected virtual int GetRpcCount() => 0; - } - - public class DummyRemoteCallCollection - { - public void Register(int id, Type type, string name, RpcInvokeType invokeType, CmdDelegate cmdDelegate, bool requireAuthority) {} - } - - public delegate void CmdDelegate(NetworkReader reader, INetworkPlayer senderConnection, int replyId); - - public static class ServerRpcSender - { - public static void Send(DummyNetworkBehaviour behaviour, int index, PooledNetworkWriter writer, int channel, bool requireAuthority) {} - } - // CodeEmbed-Start: rpc-example-change-name-generated public class Player : NetworkBehaviour { @@ -35,11 +12,14 @@ public class Player : NetworkBehaviour [ServerRpc] public void RpcChangeName(string newName) { - if (this.IsServer) + if (IsServer) + { + // direct call, skips NetworkWriter UserCode_RpcChangeName_123456789(newName); + } else { - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + using (var writer = NetworkWriterPool.GetWriter()) { writer.WriteString(newName); ServerRpcSender.Send(this, 123456789, writer, 0, true); @@ -52,17 +32,18 @@ public void UserCode_RpcChangeName_123456789(string newName) PlayerName = newName; } - protected void Skeleton_RpcChangeName_123456789(NetworkReader reader, INetworkPlayer senderConnection, int replyId) + protected static void Skeleton_RpcChangeName_123456789(NetworkBehaviour behaviour, NetworkReader reader, INetworkPlayer senderConnection, int replyId) { - this.UserCode_RpcChangeName_123456789(reader.ReadString()); + ((Player)behaviour).UserCode_RpcChangeName_123456789(reader.ReadString()); } - public Player() + protected void RegisterRpc_1(RemoteCallCollection collection) { - this.remoteCallCollection.Register(0, typeof(Player), "Player.RpcChangeName", RpcInvokeType.ServerRpc, new CmdDelegate(Skeleton_RpcChangeName_123456789), true); + base.RegisterRpc(collection); + collection.Register(0, "Mirage.Snippets.RemoteActions.RpcExamplesGenerated.Player.RpcChangeName", true, RpcInvokeType.ServerRpc, this, new RpcDelegate(Player.Skeleton_RpcChangeName_123456789), RpcRateLimitConfig.Disabled()); } - protected override int GetRpcCount() + protected int GetRpcCount_1() { return 1; } From 48c0b479cfbfb7ab9d4435abab64c80fa717d701 Mon Sep 17 00:00:00 2001 From: James Frowen Date: Tue, 2 Jun 2026 15:14:02 +0100 Subject: [PATCH 36/41] asmdef for analyzer rules, --- .../Mirage.Examples.Snippets.Analyzers.asmdef | 21 +++++++++++++++++++ ...ge.Examples.Snippets.Analyzers.asmdef.meta | 7 +++++++ 2 files changed, 28 insertions(+) create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage.Examples.Snippets.Analyzers.asmdef create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage.Examples.Snippets.Analyzers.asmdef.meta diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage.Examples.Snippets.Analyzers.asmdef b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage.Examples.Snippets.Analyzers.asmdef new file mode 100644 index 00000000000..2dd9c54d75c --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage.Examples.Snippets.Analyzers.asmdef @@ -0,0 +1,21 @@ +{ + "name": "Mirage.Examples.Snippets.Analyzers", + "rootNamespace": "", + "references": [ + "GUID:30817c1a0e6d646d99c048fc403f5979", + "GUID:f51ebe6a0ceec4240a699833d6309b23", + "GUID:96f081f4a0d214ee39e3aa34e9d43109", + "GUID:c0b2064c294eb174c9f3f7da398eb677", + "GUID:db69876fcdb4de041b3adaeefa87b6a6", + "GUID:bc6737b19bee94a798a182a84b660226" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage.Examples.Snippets.Analyzers.asmdef.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage.Examples.Snippets.Analyzers.asmdef.meta new file mode 100644 index 00000000000..9a79f5c90c9 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage.Examples.Snippets.Analyzers.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 921d822b7042138499f4bdcae4d8898d +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: From 1bf50f8f49f1d4b7b5914e59a499cf395daea66c Mon Sep 17 00:00:00 2001 From: James Frowen Date: Tue, 2 Jun 2026 15:14:31 +0100 Subject: [PATCH 37/41] feat: adding WeaverIgnoreAttribute for assemblies so Weaver will skip the whole asmdef --- .../Mirage/Runtime/Serialization/WeaverAttributes.cs | 2 +- .../Samples~/Snippets/Analyzers/AssemblyInfo.cs | 1 + .../Samples~/Snippets/Analyzers/AssemblyInfo.cs.meta | 11 +++++++++++ Assets/Mirage/Weaver/Weaver.cs | 8 ++++++++ 4 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/AssemblyInfo.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/AssemblyInfo.cs.meta diff --git a/Assets/Mirage/Runtime/Serialization/WeaverAttributes.cs b/Assets/Mirage/Runtime/Serialization/WeaverAttributes.cs index 701415575a0..3c18261a3ae 100644 --- a/Assets/Mirage/Runtime/Serialization/WeaverAttributes.cs +++ b/Assets/Mirage/Runtime/Serialization/WeaverAttributes.cs @@ -8,7 +8,7 @@ namespace Mirage.Serialization /// /// Tells Weaver to ignore a field or Method /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Assembly)] public sealed class WeaverIgnoreAttribute : Attribute { } /// diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/AssemblyInfo.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/AssemblyInfo.cs new file mode 100644 index 00000000000..275f828cd53 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/AssemblyInfo.cs @@ -0,0 +1 @@ +[assembly: Mirage.Serialization.WeaverIgnore] diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/AssemblyInfo.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/AssemblyInfo.cs.meta new file mode 100644 index 00000000000..30289882ca2 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/AssemblyInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 14837b5f7d8c5214ba459d3ced478f86 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Weaver/Weaver.cs b/Assets/Mirage/Weaver/Weaver.cs index 11a2617855c..9fddba26062 100644 --- a/Assets/Mirage/Weaver/Weaver.cs +++ b/Assets/Mirage/Weaver/Weaver.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Mirage.CodeGen; +using Mirage.Serialization; using Mono.Cecil; using Unity.CompilationPipeline.Common.ILPostProcessing; using UnityEngine; @@ -39,6 +40,13 @@ protected override ResultType Process(AssemblyDefinition assembly, ICompiledAsse Log($"Starting weaver on {compiledAssembly.Name}"); try { + var hasIgnore = assembly.HasCustomAttribute(); + if (hasIgnore) + { + Log($"Skipping {compiledAssembly.Name} because it has WeaverIgnoreAttribute"); + return ResultType.NoChanges; + } + var module = assembly.MainModule; readers = new Readers(module, logger); writers = new Writers(module, logger); From 48b2f4b9d2dacf15e302c53ff0b572a686860609 Mon Sep 17 00:00:00 2001 From: James Frowen Date: Tue, 2 Jun 2026 15:15:04 +0100 Subject: [PATCH 38/41] tmp --- .../MtuSizeEstimationTests.cs | 6 +- .../MirageAnalyzer.Performance.cs | 142 +++++++++++++----- 2 files changed, 110 insertions(+), 38 deletions(-) diff --git a/CSharpAnalyzers/Mirage.Analyzers.Tests/MtuSizeEstimationTests.cs b/CSharpAnalyzers/Mirage.Analyzers.Tests/MtuSizeEstimationTests.cs index 1af1e585de3..93da251a8dc 100644 --- a/CSharpAnalyzers/Mirage.Analyzers.Tests/MtuSizeEstimationTests.cs +++ b/CSharpAnalyzers/Mirage.Analyzers.Tests/MtuSizeEstimationTests.cs @@ -12,7 +12,7 @@ public async Task SmallMessageTriggersInfo() var code = VerifyCS.LoadTestData("MtuSizeEstimation/Positive_SmallMessageDoesNotTriggerWarning.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1501") .WithLocation(0) - .WithArguments("SmallMessage", "13"); + .WithArguments("SmallMessage", "14+ (Average ~15 + dynamic content)"); await VerifyCS.VerifyAnalyzerAsync(code, expected); } @@ -32,7 +32,7 @@ public async Task LargeMessageTriggersInfo() var code = VerifyCS.LoadTestData("MtuSizeEstimation/Negative_LargeMessageExceedsMtu.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1501") .WithLocation(0) - .WithArguments("HugeMessage", "0"); + .WithArguments("HugeMessage", "10+ (Average ~10 + dynamic content)"); await VerifyCS.VerifyAnalyzerAsync(code, expected); } @@ -43,7 +43,7 @@ public async Task RecursiveStructOrClassRefTriggersInfo() var code = VerifyCS.LoadTestData("MtuSizeEstimation/Edge_RecursiveStructOrClassRef.cs"); var expected = VerifyCS.Diagnostic("MIRAGE1501") .WithLocation(0) - .WithArguments("RecursiveMessage", "1"); + .WithArguments("RecursiveMessage", "2 to 6 (Average ~3)"); await VerifyCS.VerifyAnalyzerAsync(code, expected); } } diff --git a/CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.Performance.cs b/CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.Performance.cs index bd2d7052e03..a00646c46c2 100644 --- a/CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.Performance.cs +++ b/CSharpAnalyzers/Mirage.Analyzers/MirageAnalyzer.Performance.cs @@ -7,6 +7,45 @@ namespace Mirage.Analyzers { public partial class MirageAnalyzer { + private struct SizeEstimate + { + public int MinBits; + public int MaxBits; + public int AvgBits; + public bool HasDynamic; + + public SizeEstimate(int minBits, int maxBits, int avgBits, bool hasDynamic) + { + MinBits = minBits; + MaxBits = maxBits; + AvgBits = avgBits; + HasDynamic = hasDynamic; + } + + public void Add(SizeEstimate other) + { + MinBits += other.MinBits; + MaxBits += other.MaxBits; + AvgBits += other.AvgBits; + HasDynamic |= other.HasDynamic; + } + + public override string ToString() + { + int minBytes = (MinBits + 7) / 8; + int maxBytes = (MaxBits + 7) / 8; + int avgBytes = (AvgBits + 7) / 8; + + if (HasDynamic) + return $"{minBytes}+ (Average ~{avgBytes} + dynamic content)"; + + if (MinBits == MaxBits) + return $"{minBytes}"; + + return $"{minBytes} to {maxBytes} (Average ~{avgBytes})"; + } + } + private static void AnalyzeNamedTypePerformance(SymbolAnalysisContext context) { var typeSymbol = (INamedTypeSymbol)context.Symbol; @@ -14,113 +53,146 @@ private static void AnalyzeNamedTypePerformance(SymbolAnalysisContext context) if (MirageAttributes.NetworkMessage.Has(typeSymbol)) { var visited = new HashSet(SymbolEqualityComparer.Default); - int estimatedSize = EstimateSerializedSize(typeSymbol, visited); - var diagnostic = Diagnostic.Create(MirageRules.PerformanceMessageSizeRule, typeSymbol.Locations[0], typeSymbol.Name, estimatedSize); + var estimatedSize = EstimateSerializedSize(typeSymbol, visited); + var diagnostic = Diagnostic.Create( + MirageRules.PerformanceMessageSizeRule, + typeSymbol.Locations[0], + typeSymbol.Name, + estimatedSize.ToString()); context.ReportDiagnostic(diagnostic); } } - private static int EstimateSerializedSize(ITypeSymbol type, HashSet visited) + private static SizeEstimate EstimateSerializedSize(ITypeSymbol type, HashSet visited) { if (type == null) - return 0; + return new SizeEstimate(0, 0, 0, false); if (!visited.Add(type)) - return 0; + return new SizeEstimate(0, 0, 0, false); if (MirageTypes.GameObject.IsOrInherits(type) || MirageTypes.NetworkBehaviour.IsOrInherits(type) || MirageTypes.NetworkIdentity.IsOrInherits(type)) { - return 2; + // Mirage identities and behaviors use packed netId + component index, which varies in size + return new SizeEstimate(16, 80, 24, false); } switch (type.SpecialType) { case SpecialType.System_Boolean: + return new SizeEstimate(1, 1, 1, false); case SpecialType.System_Byte: case SpecialType.System_SByte: - return 1; + return new SizeEstimate(8, 8, 8, false); case SpecialType.System_Char: - return 2; + return new SizeEstimate(16, 16, 16, false); case SpecialType.System_Int16: case SpecialType.System_UInt16: - return 2; + return new SizeEstimate(16, 16, 16, false); case SpecialType.System_Int32: case SpecialType.System_UInt32: - return 1; // 1 byte default for typical uint/int + // Packed uint32 uses variable length zigzag encoding + return new SizeEstimate(8, 40, 16, false); case SpecialType.System_Int64: case SpecialType.System_UInt64: - return 8; + // Packed uint64 uses variable length zigzag encoding + return new SizeEstimate(8, 72, 24, false); case SpecialType.System_Single: - return 4; + return new SizeEstimate(32, 32, 32, false); case SpecialType.System_Double: - return 8; + return new SizeEstimate(64, 64, 64, false); case SpecialType.System_Decimal: - return 16; + return new SizeEstimate(128, 128, 128, false); case SpecialType.System_String: - return 0; // Dynamic type: skip/treat as variable + // Strings are dynamic and length prefix takes 1 to 5 bytes + return new SizeEstimate(8, 8, 8, true); } if (type.ContainingNamespace?.ToDisplayString() == "UnityEngine") { switch (type.Name) { - case "Vector2": return 8; - case "Vector3": return 12; - case "Vector4": return 16; - case "Quaternion": return 16; - case "Color": return 16; - case "Color32": return 4; - case "Vector2Int": return 2; - case "Vector3Int": return 3; + case "Vector2": + return new SizeEstimate(64, 64, 64, false); + case "Vector3": + return new SizeEstimate(96, 96, 96, false); + case "Vector4": + return new SizeEstimate(128, 128, 128, false); + case "Quaternion": + return new SizeEstimate(128, 128, 128, false); + case "Color": + return new SizeEstimate(128, 128, 128, false); + case "Color32": + return new SizeEstimate(32, 32, 32, false); + case "Vector2Int": + // Two packed int32 values + return new SizeEstimate(16, 80, 32, false); + case "Vector3Int": + // Three packed int32 values + return new SizeEstimate(24, 120, 48, false); } } if (type.TypeKind == TypeKind.Enum) { var underlying = (type as INamedTypeSymbol)?.EnumUnderlyingType; - return underlying != null ? EstimateSerializedSize(underlying, visited) : 1; + return underlying != null ? EstimateSerializedSize(underlying, visited) : new SizeEstimate(8, 40, 16, false); } if (type is IArrayTypeSymbol arrayType) - return 0; // Dynamic type: skip/treat as variable + // Collections are dynamic and have a var-int length prefix + return new SizeEstimate(8, 8, 8, true); if (type is INamedTypeSymbol namedType && namedType.IsGenericType) { if (MirageTypes.IEnumerable.Implements(namedType)) - { - return 0; // Dynamic type: skip/treat as variable - } + return new SizeEstimate(8, 8, 8, true); } if (type.TypeKind == TypeKind.Struct || type.TypeKind == TypeKind.Class) { - int sum = 0; + var sum = new SizeEstimate(0, 0, 0, false); foreach (var member in type.GetMembers()) { if (member is IFieldSymbol field && !field.IsStatic && field.DeclaredAccessibility == Accessibility.Public) - sum += EstimateMemberSerializedSize(field, field.Type, visited); + { + var memberEstimate = EstimateMemberSerializedSize(field, field.Type, visited); + sum.Add(memberEstimate); + } + } + + if (type.TypeKind == TypeKind.Class) + { + // Add 1 bit for null-check bool prefix + sum.MinBits += 1; + sum.MaxBits += 1; + sum.AvgBits += 1; } return sum; } - return 0; + return new SizeEstimate(0, 0, 0, false); } - private static int EstimateMemberSerializedSize(ISymbol symbol, ITypeSymbol type, HashSet visited) + private static SizeEstimate EstimateMemberSerializedSize(ISymbol symbol, ITypeSymbol type, HashSet visited) { if (MirageAttributes.BitCount.TryGet(symbol, out var bitCountAttr) && bitCountAttr.ConstructorArguments.Length > 0) + { if (bitCountAttr.ConstructorArguments[0].Value is int bits) - return (bits + 7) / 8; + return new SizeEstimate(bits, bits, bits, false); + } if (MirageAttributes.FloatPack.TryGet(symbol, out var floatPackAttr)) { if (floatPackAttr.ConstructorArguments.Length >= 2) + { if (floatPackAttr.ConstructorArguments[1].Value is int bits) - return (bits + 7) / 8; + return new SizeEstimate(bits, bits, bits, false); + } - return 4; + return new SizeEstimate(32, 32, 32, false); } return EstimateSerializedSize(type, visited); From 2883da2a73813bfccd70b05b3e2462b28f1f8012 Mon Sep 17 00:00:00 2001 From: James Frowen Date: Thu, 11 Jun 2026 16:45:16 +0100 Subject: [PATCH 39/41] remove RoslynAnalyzer label until analyzer works --- Assets/Mirage/Analyzers/Mirage.Analyzers.dll.meta | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Assets/Mirage/Analyzers/Mirage.Analyzers.dll.meta b/Assets/Mirage/Analyzers/Mirage.Analyzers.dll.meta index 36b7f968cb3..b279fe7192d 100644 --- a/Assets/Mirage/Analyzers/Mirage.Analyzers.dll.meta +++ b/Assets/Mirage/Analyzers/Mirage.Analyzers.dll.meta @@ -1,7 +1,6 @@ fileFormatVersion: 2 guid: 4a3b7d1e0c2f4e8b8a5d8f6c6f7e8a9b -labels: -- RoslynAnalyzer +labels: [] PluginImporter: externalObjects: {} serializedVersion: 2 From db7bed560898030c503575f4b4861a2e132d50fd Mon Sep 17 00:00:00 2001 From: James Frowen Date: Thu, 11 Jun 2026 16:47:19 +0100 Subject: [PATCH 40/41] fix merge issues with syncvar properties --- Assets/Mirage/Runtime/CustomAttributes.cs | 2 +- .../Runtime/SpatialHashBenchmark/Scripts/AddChild.cs | 2 +- .../Runtime/SpatialHashBenchmark/Scripts/Monster.cs | 5 ++--- .../SpatialHashBenchmark/Scripts/PlayerCharacter.cs | 9 ++++----- Assets/Tests/Runtime/Serialization/MaxLengthTests.cs | 2 +- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Assets/Mirage/Runtime/CustomAttributes.cs b/Assets/Mirage/Runtime/CustomAttributes.cs index b6e801bb5a2..298d99eb81e 100644 --- a/Assets/Mirage/Runtime/CustomAttributes.cs +++ b/Assets/Mirage/Runtime/CustomAttributes.cs @@ -317,7 +317,7 @@ public sealed class ShowSyncSettingsAttribute : Attribute { } /// or any custom type that has read/write overloads accepting an integer limit. /// This will use the Write/Read with length functions and will work on any type that has writers/readers for those. /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field)] + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] public class MaxLengthAttribute : Attribute { public readonly int maxLength; diff --git a/Assets/Tests/Performance/Runtime/SpatialHashBenchmark/Scripts/AddChild.cs b/Assets/Tests/Performance/Runtime/SpatialHashBenchmark/Scripts/AddChild.cs index a64543ef255..7d20d3cabea 100644 --- a/Assets/Tests/Performance/Runtime/SpatialHashBenchmark/Scripts/AddChild.cs +++ b/Assets/Tests/Performance/Runtime/SpatialHashBenchmark/Scripts/AddChild.cs @@ -4,7 +4,7 @@ public class AddChild : NetworkBehaviour { public NetworkIdentity prefab; [SyncVar] - private NetworkIdentity clone; + private NetworkIdentity clone { get; set; } private void Awake() { diff --git a/Assets/Tests/Performance/Runtime/SpatialHashBenchmark/Scripts/Monster.cs b/Assets/Tests/Performance/Runtime/SpatialHashBenchmark/Scripts/Monster.cs index 228992518e9..9e5096c783d 100644 --- a/Assets/Tests/Performance/Runtime/SpatialHashBenchmark/Scripts/Monster.cs +++ b/Assets/Tests/Performance/Runtime/SpatialHashBenchmark/Scripts/Monster.cs @@ -1,5 +1,4 @@ using System; -using Mirage; using Mirage.SocketLayer; namespace Mirage.Tests.Performance.Runtime.SpatialHashBenchmark @@ -7,9 +6,9 @@ namespace Mirage.Tests.Performance.Runtime.SpatialHashBenchmark public class Monster : NetworkBehaviour { [SyncVar] - public float Speed; + public float Speed { get; set; } [SyncVar] - public int Health; + public int Health { get; set; } public bool TakeDamage(int damage) diff --git a/Assets/Tests/Performance/Runtime/SpatialHashBenchmark/Scripts/PlayerCharacter.cs b/Assets/Tests/Performance/Runtime/SpatialHashBenchmark/Scripts/PlayerCharacter.cs index 9a7d7b1e25e..c5d0976ed4a 100644 --- a/Assets/Tests/Performance/Runtime/SpatialHashBenchmark/Scripts/PlayerCharacter.cs +++ b/Assets/Tests/Performance/Runtime/SpatialHashBenchmark/Scripts/PlayerCharacter.cs @@ -1,4 +1,3 @@ -using Mirage; using UnityEngine; using Random = UnityEngine.Random; @@ -9,13 +8,13 @@ public class PlayerCharacter : NetworkBehaviour public float SpawnRadius; [SyncVar] - public float Speed = 3; + public float Speed { get; set; } = 3; [SyncVar] - public int Damage = 1; + public int Damage { get; set; } = 1; [SyncVar] - public int Level = 1; + public int Level { get; set; } = 1; [SyncVar] - public int XP; + public int XP { get; set; } private void Awake() diff --git a/Assets/Tests/Runtime/Serialization/MaxLengthTests.cs b/Assets/Tests/Runtime/Serialization/MaxLengthTests.cs index db906ccf51e..21648582f47 100644 --- a/Assets/Tests/Runtime/Serialization/MaxLengthTests.cs +++ b/Assets/Tests/Runtime/Serialization/MaxLengthTests.cs @@ -54,7 +54,7 @@ public void SendStringClient2([MaxLength(150)] string message) public class SyncVarBehaviour : NetworkBehaviour { [SyncVar, MaxLength(6)] - public string Content; + public string Content { get; set; } } public class DummyBehaviour : NetworkBehaviour From b4592ff44ed7a15f8fa63bdf60948ac84ebcc87f Mon Sep 17 00:00:00 2001 From: James Frowen Date: Sun, 28 Jun 2026 12:52:35 +0100 Subject: [PATCH 41/41] check for server.enabled --- .../Samples~/Snippets/Analyzers/Mirage1403.cs | 55 +++++++++++++++++++ .../Snippets/Analyzers/Mirage1403.cs.meta | 11 ++++ doc/docs/analyzers/MIRAGE1403.md | 25 +++++++++ doc/docs/analyzers/index.md | 4 ++ 4 files changed, 95 insertions(+) create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1403.cs create mode 100644 Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1403.cs.meta create mode 100644 doc/docs/analyzers/MIRAGE1403.md diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1403.cs b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1403.cs new file mode 100644 index 00000000000..9c2d5971d8c --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1403.cs @@ -0,0 +1,55 @@ +using Mirage; +using UnityEngine; + +namespace Mirage.Snippets.Analyzers +{ + namespace M1403.Triggering + { + // CodeEmbed-Start: mirage1403-triggering + public class ServerStatusCheck : MonoBehaviour + { + public NetworkServer Server; + public NetworkClient Client; + + private void Update() + { + // Warning: Checking .enabled checks Unity Component status, not if the server/client is active + if (Server.enabled) + { + // Server logic... + } + + if (Client.enabled) + { + // Client logic... + } + } + } + // CodeEmbed-End: mirage1403-triggering + } + + namespace M1403.Resolved + { + // CodeEmbed-Start: mirage1403-resolved + public class ServerStatusCheck : MonoBehaviour + { + public NetworkServer Server; + public NetworkClient Client; + + private void Update() + { + // Correct: Use .Active to check if the server/client is actively running + if (Server.Active) + { + // Server logic... + } + + if (Client.Active) + { + // Client logic... + } + } + } + // CodeEmbed-End: mirage1403-resolved + } +} diff --git a/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1403.cs.meta b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1403.cs.meta new file mode 100644 index 00000000000..01d0b882512 --- /dev/null +++ b/Assets/Mirage/Samples~/Snippets/Analyzers/Mirage1403.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 35abfac5ac11e904bb71f6c137a3fc70 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/doc/docs/analyzers/MIRAGE1403.md b/doc/docs/analyzers/MIRAGE1403.md new file mode 100644 index 00000000000..65227fc0984 --- /dev/null +++ b/doc/docs/analyzers/MIRAGE1403.md @@ -0,0 +1,25 @@ +# MIRAGE1403: Enabled property check on NetworkServer/Client + +## The Problem + +Checking or setting the `.enabled` property on a `NetworkServer` or `NetworkClient` reference: + +* `NetworkServer.enabled` +* `NetworkClient.enabled` + +`NetworkServer` and `NetworkClient` both inherit from `MonoBehaviour`, which inherits the `.enabled` property from Unity's `Behaviour` class. Checking `.enabled` only checks if the MonoBehaviour component is enabled or disabled in the Unity Inspector. It does **not** reflect whether the server or client is actively running or connected. + +Accessing `.enabled` to check server or client status will lead to incorrect state logic (e.g. returning `true` even if the server is stopped but the component remains enabled). + +--- + +## Example of Triggering Code +{{{ Path:'Snippets/Analyzers/Mirage1403.cs' Name:'mirage1403-triggering' }}} + +--- + +## How to Resolve + +Use the `.Active` property on `NetworkServer` or `NetworkClient` to check whether the server is listening for connections or if the client is connected to a server. + +{{{ Path:'Snippets/Analyzers/Mirage1403.cs' Name:'mirage1403-resolved' }}} diff --git a/doc/docs/analyzers/index.md b/doc/docs/analyzers/index.md index 4e1f0d78216..3771a3ec5f5 100644 --- a/doc/docs/analyzers/index.md +++ b/doc/docs/analyzers/index.md @@ -29,6 +29,7 @@ Mirage uses Roslyn Analyzers to provide compile-time validation for network code | [MIRAGE1305](MIRAGE1305.md) | Missing NetworkMessage Attribute | Warning | Warns if a type is sent or registered as a message, but lacks the `[NetworkMessage]` attribute. | | [MIRAGE1401](MIRAGE1401.md) | Accessing Network State in Awake/Start | Warning | Warns against accessing network states like `IsServer` during early Unity lifecycle phases. | | [MIRAGE1402](MIRAGE1402.md) | Missing base Call in OnSerialize/OnDeserialize | Warning | Ensures overriding `OnSerialize` or `OnDeserialize` in derived classes calls the base implementation. | +| [MIRAGE1403](MIRAGE1403.md) | Enabled property check on NetworkServer/Client | Warning | Warns if checking/setting .enabled on NetworkServer/Client instead of using .Active. | | [MIRAGE1501](MIRAGE1501.md) | Network Message Serialized Size Estimation | Info | Estimates the serialized size of all `[NetworkMessage]` types to help analyze bandwidth usage. | --- @@ -116,6 +117,9 @@ Accessing network properties like `IsServer`, `IsClient`, or authority states in #### [MIRAGE1402: Missing base Call in OnSerialize/OnDeserialize](MIRAGE1402.md) Derived classes overriding custom `OnSerialize` or `OnDeserialize` methods must call their base class implementations if the base class also synchronizes state. Failing to call the base method prevents base `SyncVars` and properties from synchronizing properly. Fix this by calling the base method and combining their return values. +#### [MIRAGE1403: Enabled property check on NetworkServer/Client](MIRAGE1403.md) +Warns if a developer tries to check whether `NetworkServer` or `NetworkClient` is running by accessing its `.enabled` property. Since both inherit from `MonoBehaviour`, `.enabled` is just Unity's component active flag, which is not synchronized with the active server/client state. Use the `.Active` property instead. + --- ### Group 6: Performance & Size Estimation (`MIRAGE1500 – MIRAGE1599`)