Initial Commit

This commit is contained in:
Sebastian Cabrera 2021-08-02 05:44:37 -04:00
parent 53eb92e9af
commit 270ab7d11f
15341 changed files with 700234 additions and 0 deletions

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bbb4974b4302f435b9f4663c64d8f803
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,196 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using MLAPI.Messaging;
using MLAPI.Serialization;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
using Unity.CompilationPipeline.Common.Diagnostics;
using Unity.CompilationPipeline.Common.ILPostProcessing;
using UnityEngine;
#if !UNITY_2019_4_OR_NEWER
#error MLAPI requires Unity 2019.4 or newer
#endif
namespace MLAPI.Editor.CodeGen
{
internal static class CodeGenHelpers
{
public const string RuntimeAssemblyName = "Unity.Multiplayer.MLAPI.Runtime";
public static readonly string NetworkBehaviour_FullName = typeof(NetworkBehaviour).FullName;
public static readonly string ServerRpcAttribute_FullName = typeof(ServerRpcAttribute).FullName;
public static readonly string ClientRpcAttribute_FullName = typeof(ClientRpcAttribute).FullName;
public static readonly string ServerRpcParams_FullName = typeof(ServerRpcParams).FullName;
public static readonly string ClientRpcParams_FullName = typeof(ClientRpcParams).FullName;
public static readonly string INetworkSerializable_FullName = typeof(INetworkSerializable).FullName;
public static readonly string INetworkSerializable_NetworkSerialize_Name = nameof(INetworkSerializable.NetworkSerialize);
public static readonly string UnityColor_FullName = typeof(Color).FullName;
public static readonly string UnityColor32_FullName = typeof(Color32).FullName;
public static readonly string UnityVector2_FullName = typeof(Vector2).FullName;
public static readonly string UnityVector3_FullName = typeof(Vector3).FullName;
public static readonly string UnityVector4_FullName = typeof(Vector4).FullName;
public static readonly string UnityQuaternion_FullName = typeof(Quaternion).FullName;
public static readonly string UnityRay_FullName = typeof(Ray).FullName;
public static readonly string UnityRay2D_FullName = typeof(Ray2D).FullName;
public static uint Hash(this MethodDefinition methodDefinition)
{
var sigArr = Encoding.UTF8.GetBytes($"{methodDefinition.Module.Name} / {methodDefinition.FullName}");
var sigLen = sigArr.Length;
unsafe
{
fixed (byte* sigPtr = sigArr)
{
return XXHash.Hash32(sigPtr, sigLen);
}
}
}
public static bool IsSubclassOf(this TypeDefinition typeDefinition, string ClassTypeFullName)
{
if (!typeDefinition.IsClass) return false;
var baseTypeRef = typeDefinition.BaseType;
while (baseTypeRef != null)
{
if (baseTypeRef.FullName == ClassTypeFullName)
{
return true;
}
try
{
baseTypeRef = baseTypeRef.Resolve().BaseType;
}
catch
{
return false;
}
}
return false;
}
public static bool HasInterface(this TypeReference typeReference, string InterfaceTypeFullName)
{
if (typeReference.IsArray) return false;
try
{
var typeDef = typeReference.Resolve();
var typeFaces = typeDef.Interfaces;
return typeFaces.Any(iface => iface.InterfaceType.FullName == InterfaceTypeFullName);
}
catch
{
return false;
}
}
public static bool IsSerializable(this TypeReference typeReference)
{
var typeSystem = typeReference.Module.TypeSystem;
// C# primitives
if (typeReference == typeSystem.Boolean) return true;
if (typeReference == typeSystem.Char) return true;
if (typeReference == typeSystem.SByte) return true;
if (typeReference == typeSystem.Byte) return true;
if (typeReference == typeSystem.Int16) return true;
if (typeReference == typeSystem.UInt16) return true;
if (typeReference == typeSystem.Int32) return true;
if (typeReference == typeSystem.UInt32) return true;
if (typeReference == typeSystem.Int64) return true;
if (typeReference == typeSystem.UInt64) return true;
if (typeReference == typeSystem.Single) return true;
if (typeReference == typeSystem.Double) return true;
if (typeReference == typeSystem.String) return true;
// Unity primitives
if (typeReference.FullName == UnityColor_FullName) return true;
if (typeReference.FullName == UnityColor32_FullName) return true;
if (typeReference.FullName == UnityVector2_FullName) return true;
if (typeReference.FullName == UnityVector3_FullName) return true;
if (typeReference.FullName == UnityVector4_FullName) return true;
if (typeReference.FullName == UnityQuaternion_FullName) return true;
if (typeReference.FullName == UnityRay_FullName) return true;
if (typeReference.FullName == UnityRay2D_FullName) return true;
// Enum
if (typeReference.GetEnumAsInt() != null) return true;
// INetworkSerializable
if (typeReference.HasInterface(INetworkSerializable_FullName)) return true;
// Static array
if (typeReference.IsArray) return typeReference.GetElementType().IsSerializable();
return false;
}
public static TypeReference GetEnumAsInt(this TypeReference typeReference)
{
if (typeReference.IsArray) return null;
try
{
var typeDef = typeReference.Resolve();
return typeDef.IsEnum ? typeDef.GetEnumUnderlyingType() : null;
}
catch
{
return null;
}
}
public static void AddError(this List<DiagnosticMessage> diagnostics, string message)
{
diagnostics.AddError((SequencePoint)null, message);
}
public static void AddError(this List<DiagnosticMessage> diagnostics, MethodDefinition methodDefinition, string message)
{
diagnostics.AddError(methodDefinition.DebugInformation.SequencePoints.FirstOrDefault(), message);
}
public static void AddError(this List<DiagnosticMessage> diagnostics, SequencePoint sequencePoint, string message)
{
diagnostics.Add(new DiagnosticMessage
{
DiagnosticType = DiagnosticType.Error,
File = sequencePoint?.Document.Url.Replace($"{Environment.CurrentDirectory}{Path.DirectorySeparatorChar}", ""),
Line = sequencePoint?.StartLine ?? 0,
Column = sequencePoint?.StartColumn ?? 0,
MessageData = $" - {message}"
});
}
public static AssemblyDefinition AssemblyDefinitionFor(ICompiledAssembly compiledAssembly)
{
var assemblyResolver = new PostProcessorAssemblyResolver(compiledAssembly);
var readerParameters = new ReaderParameters
{
SymbolStream = new MemoryStream(compiledAssembly.InMemoryAssembly.PdbData),
SymbolReaderProvider = new PortablePdbReaderProvider(),
AssemblyResolver = assemblyResolver,
ReflectionImporterProvider = new PostProcessorReflectionImporterProvider(),
ReadingMode = ReadingMode.Immediate
};
var assemblyDefinition = AssemblyDefinition.ReadAssembly(new MemoryStream(compiledAssembly.InMemoryAssembly.PeData), readerParameters);
//apparently, it will happen that when we ask to resolve a type that lives inside MLAPI.Runtime, and we
//are also postprocessing MLAPI.Runtime, type resolving will fail, because we do not actually try to resolve
//inside the assembly we are processing. Let's make sure we do that, so that we can use postprocessor features inside
//MLAPI.Runtime itself as well.
assemblyResolver.AddAssemblyDefinitionBeingOperatedOn(assemblyDefinition);
return assemblyDefinition;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0e5541b3bca0e43b48c2e694fffef5b3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,42 @@
#if !UNITY_2020_2_OR_NEWER
using System.IO;
using Unity.CompilationPipeline.Common.ILPostProcessing;
namespace MLAPI.Editor.CodeGen
{
internal class ILPostProcessCompiledAssembly : ICompiledAssembly
{
private readonly string m_AssemblyFilename;
private readonly string m_OutputPath;
private InMemoryAssembly m_InMemoryAssembly;
public ILPostProcessCompiledAssembly(string asmName, string[] refs, string[] defines, string outputPath)
{
m_AssemblyFilename = asmName;
Name = Path.GetFileNameWithoutExtension(m_AssemblyFilename);
References = refs;
Defines = defines;
m_OutputPath = outputPath;
}
public string Name { get; }
public string[] References { get; }
public string[] Defines { get; }
public InMemoryAssembly InMemoryAssembly
{
get
{
if (m_InMemoryAssembly == null)
{
m_InMemoryAssembly = new InMemoryAssembly(
File.ReadAllBytes(Path.Combine(m_OutputPath, m_AssemblyFilename)),
File.ReadAllBytes(Path.Combine(m_OutputPath, $"{Path.GetFileNameWithoutExtension(m_AssemblyFilename)}.pdb")));
}
return m_InMemoryAssembly;
}
}
}
}
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c63d199856aa44f4581ec4de75bf3f44
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,13 @@
#if !UNITY_2020_2_OR_NEWER
using Unity.CompilationPipeline.Common.ILPostProcessing;
namespace MLAPI.Editor.CodeGen
{
public abstract class ILPostProcessor
{
public abstract bool WillProcess(ICompiledAssembly compiledAssembly);
public abstract ILPostProcessResult Process(ICompiledAssembly compiledAssembly);
public abstract ILPostProcessor GetInstance();
}
}
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cbffd9b273517da4a9c4a3218771e0b5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,239 @@
#if !UNITY_2020_2_OR_NEWER
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Unity.CompilationPipeline.Common.Diagnostics;
using Unity.CompilationPipeline.Common.ILPostProcessing;
using UnityEditor;
using UnityEditor.Compilation;
using UnityEngine;
using Assembly = System.Reflection.Assembly;
using ILPPInterface = Unity.CompilationPipeline.Common.ILPostProcessing.ILPostProcessor;
namespace MLAPI.Editor.CodeGen
{
// There is a behaviour difference between 2019.4 and 2020+ codegen
// that essentially does checking on the existence of ILPP vs if a CodeGen assembly
// is present. So in order to make sure ILPP runs properly in 2019.4 from a clean
// import of the project we add this dummy ILPP which forces the callback to made
// and meets the internal ScriptCompilation pipeline requirements
internal sealed class ILPP2019CodegenWorkaround : ILPPInterface
{
public override ILPPInterface GetInstance()
{
return this;
}
public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly)
{
return null;
}
public override bool WillProcess(ICompiledAssembly compiledAssembly) => compiledAssembly.References.Any(filePath => Path.GetFileNameWithoutExtension(filePath) == CodeGenHelpers.RuntimeAssemblyName);
}
internal static class ILPostProcessProgram
{
private static ILPostProcessor[] s_ILPostProcessors { get; set; }
[InitializeOnLoadMethod]
private static void OnInitializeOnLoad()
{
CompilationPipeline.assemblyCompilationFinished += OnCompilationFinished;
s_ILPostProcessors = FindAllPostProcessors();
}
private static ILPostProcessor[] FindAllPostProcessors()
{
var typesDerivedFrom = TypeCache.GetTypesDerivedFrom<ILPostProcessor>();
var localILPostProcessors = new List<ILPostProcessor>(typesDerivedFrom.Count);
foreach (var typeCollection in typesDerivedFrom)
{
try
{
localILPostProcessors.Add((ILPostProcessor)Activator.CreateInstance(typeCollection));
}
catch (Exception exception)
{
Debug.LogError($"Could not create {nameof(ILPostProcessor)} ({typeCollection.FullName}):{Environment.NewLine}{exception.StackTrace}");
}
}
// Default sort by type full name
localILPostProcessors.Sort((left, right) => string.Compare(left.GetType().FullName, right.GetType().FullName, StringComparison.Ordinal));
return localILPostProcessors.ToArray();
}
private static void OnCompilationFinished(string targetAssembly, CompilerMessage[] messages)
{
if (messages.Length > 0)
{
if (messages.Any(msg => msg.type == CompilerMessageType.Error))
{
return;
}
}
// Should not run on the editor only assemblies
if (targetAssembly.Contains("-Editor") || targetAssembly.Contains(".Editor"))
{
return;
}
// Should not run on Unity Engine modules but we can run on the MLAPI Runtime DLL
if ((targetAssembly.Contains("com.unity") || Path.GetFileName(targetAssembly).StartsWith("Unity")) && !targetAssembly.Contains("Unity.Multiplayer."))
{
return;
}
// Debug.Log($"Running MLAPI ILPP on {targetAssembly}");
var outputDirectory = $"{Application.dataPath}/../{Path.GetDirectoryName(targetAssembly)}";
var unityEngine = string.Empty;
var mlapiRuntimeAssemblyPath = string.Empty;
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
var usesMLAPI = false;
var foundThisAssembly = false;
var depenencyPaths = new List<string>();
foreach (var assembly in assemblies)
{
// Find the assembly currently being compiled from domain assembly list and check if it's using unet
if (assembly.GetName().Name == Path.GetFileNameWithoutExtension(targetAssembly))
{
foundThisAssembly = true;
foreach (var dependency in assembly.GetReferencedAssemblies())
{
// Since this assembly is already loaded in the domain this is a no-op and returns the
// already loaded assembly
depenencyPaths.Add(Assembly.Load(dependency).Location);
if (dependency.Name.Contains(CodeGenHelpers.RuntimeAssemblyName))
{
usesMLAPI = true;
}
}
}
try
{
if (assembly.Location.Contains("UnityEngine.CoreModule"))
{
unityEngine = assembly.Location;
}
if (assembly.Location.Contains(CodeGenHelpers.RuntimeAssemblyName))
{
mlapiRuntimeAssemblyPath = assembly.Location;
}
}
catch (NotSupportedException)
{
// in memory assembly, can't get location
}
}
if (!foundThisAssembly)
{
// Target assembly not found in current domain, trying to load it to check references
// will lead to trouble in the build pipeline, so lets assume it should go to weaver.
// Add all assemblies in current domain to dependency list since there could be a
// dependency lurking there (there might be generated assemblies so ignore file not found exceptions).
// (can happen in runtime test framework on editor platform and when doing full library reimport)
foreach (var assembly in assemblies)
{
try
{
if (!(assembly.ManifestModule is System.Reflection.Emit.ModuleBuilder))
{
depenencyPaths.Add(Assembly.Load(assembly.GetName().Name).Location);
}
}
catch (FileNotFoundException)
{
}
}
usesMLAPI = true;
}
// We check if we are the MLAPI!
if (!usesMLAPI)
{
// we shall also check and see if it we are ourself
usesMLAPI = targetAssembly.Contains(CodeGenHelpers.RuntimeAssemblyName);
}
if (!usesMLAPI)
{
return;
}
if (string.IsNullOrEmpty(unityEngine))
{
Debug.LogError("Failed to find UnityEngine assembly");
return;
}
if (string.IsNullOrEmpty(mlapiRuntimeAssemblyPath))
{
Debug.LogError("Failed to find mlapi runtime assembly");
return;
}
var assemblyPathName = Path.GetFileName(targetAssembly);
var targetCompiledAssembly = new ILPostProcessCompiledAssembly(assemblyPathName, depenencyPaths.ToArray(), null, outputDirectory);
void WriteAssembly(InMemoryAssembly inMemoryAssembly, string outputPath, string assName)
{
if (inMemoryAssembly == null)
{
throw new ArgumentException("InMemoryAssembly has never been accessed or modified");
}
var asmPath = Path.Combine(outputPath, assName);
var pdbFileName = $"{Path.GetFileNameWithoutExtension(assName)}.pdb";
var pdbPath = Path.Combine(outputPath, pdbFileName);
File.WriteAllBytes(asmPath, inMemoryAssembly.PeData);
File.WriteAllBytes(pdbPath, inMemoryAssembly.PdbData);
}
foreach (var i in s_ILPostProcessors)
{
var result = i.Process(targetCompiledAssembly);
if (result == null) continue;
if (result.Diagnostics.Count > 0)
{
Debug.LogError($"{nameof(ILPostProcessor)} - {i.GetType().Name} failed to run on {targetCompiledAssembly.Name}");
foreach (var message in result.Diagnostics)
{
switch (message.DiagnosticType)
{
case DiagnosticType.Error:
Debug.LogError($"{nameof(ILPostProcessor)} Error - {message.MessageData} {message.File}:{message.Line}");
break;
case DiagnosticType.Warning:
Debug.LogWarning($"{nameof(ILPostProcessor)} Warning - {message.MessageData} {message.File}:{message.Line}");
break;
}
}
continue;
}
// we now need to write out the result?
WriteAssembly(result.InMemoryAssembly, outputDirectory, assemblyPathName);
}
}
}
}
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 72c7d2bd3d748db4d988e2204fe1083e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cf1c8b78182704372820a586c1c91d97
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,139 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using Mono.Cecil;
using Unity.CompilationPipeline.Common.ILPostProcessing;
namespace MLAPI.Editor.CodeGen
{
internal class PostProcessorAssemblyResolver : IAssemblyResolver
{
private readonly string[] m_AssemblyReferences;
private readonly Dictionary<string, AssemblyDefinition> m_AssemblyCache = new Dictionary<string, AssemblyDefinition>();
private readonly ICompiledAssembly m_CompiledAssembly;
private AssemblyDefinition m_SelfAssembly;
public PostProcessorAssemblyResolver(ICompiledAssembly compiledAssembly)
{
m_CompiledAssembly = compiledAssembly;
m_AssemblyReferences = compiledAssembly.References;
}
public void Dispose() { }
public AssemblyDefinition Resolve(AssemblyNameReference name) => Resolve(name, new ReaderParameters(ReadingMode.Deferred));
public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
{
lock (m_AssemblyCache)
{
if (name.Name == m_CompiledAssembly.Name)
{
return m_SelfAssembly;
}
var fileName = FindFile(name);
if (fileName == null)
{
return null;
}
var lastWriteTime = File.GetLastWriteTime(fileName);
var cacheKey = $"{fileName}{lastWriteTime}";
if (m_AssemblyCache.TryGetValue(cacheKey, out var result))
{
return result;
}
parameters.AssemblyResolver = this;
var ms = MemoryStreamFor(fileName);
var pdb = $"{fileName}.pdb";
if (File.Exists(pdb))
{
parameters.SymbolStream = MemoryStreamFor(pdb);
}
var assemblyDefinition = AssemblyDefinition.ReadAssembly(ms, parameters);
m_AssemblyCache.Add(cacheKey, assemblyDefinition);
return assemblyDefinition;
}
}
private string FindFile(AssemblyNameReference name)
{
var fileName = m_AssemblyReferences.FirstOrDefault(r => Path.GetFileName(r) == $"{name.Name}.dll");
if (fileName != null)
{
return fileName;
}
// perhaps the type comes from an exe instead
fileName = m_AssemblyReferences.FirstOrDefault(r => Path.GetFileName(r) == $"{name.Name}.exe");
if (fileName != null)
{
return fileName;
}
//Unfortunately the current ICompiledAssembly API only provides direct references.
//It is very much possible that a postprocessor ends up investigating a type in a directly
//referenced assembly, that contains a field that is not in a directly referenced assembly.
//if we don't do anything special for that situation, it will fail to resolve. We should fix this
//in the ILPostProcessing API. As a workaround, we rely on the fact here that the indirect references
//are always located next to direct references, so we search in all directories of direct references we
//got passed, and if we find the file in there, we resolve to it.
return m_AssemblyReferences
.Select(Path.GetDirectoryName)
.Distinct()
.Select(parentDir => Path.Combine(parentDir, $"{name.Name}.dll"))
.FirstOrDefault(File.Exists);
}
private static MemoryStream MemoryStreamFor(string fileName)
{
return Retry(10, TimeSpan.FromSeconds(1), () =>
{
byte[] byteArray;
using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
byteArray = new byte[fs.Length];
var readLength = fs.Read(byteArray, 0, (int)fs.Length);
if (readLength != fs.Length)
{
throw new InvalidOperationException("File read length is not full length of file.");
}
}
return new MemoryStream(byteArray);
});
}
private static MemoryStream Retry(int retryCount, TimeSpan waitTime, Func<MemoryStream> func)
{
try
{
return func();
}
catch (IOException)
{
if (retryCount == 0)
{
throw;
}
Console.WriteLine($"Caught IO Exception, trying {retryCount} more times");
Thread.Sleep(waitTime);
return Retry(retryCount - 1, waitTime, func);
}
}
public void AddAssemblyDefinitionBeingOperatedOn(AssemblyDefinition assemblyDefinition)
{
m_SelfAssembly = assemblyDefinition;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2c247f4266b2864eb96e6a9ae6557d31
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,22 @@
using System.Linq;
using System.Reflection;
using Mono.Cecil;
namespace MLAPI.Editor.CodeGen
{
internal class PostProcessorReflectionImporter : DefaultReflectionImporter
{
private const string k_SystemPrivateCoreLib = "System.Private.CoreLib";
private readonly AssemblyNameReference m_CorrectCorlib;
public PostProcessorReflectionImporter(ModuleDefinition module) : base(module)
{
m_CorrectCorlib = module.AssemblyReferences.FirstOrDefault(a => a.Name == "mscorlib" || a.Name == "netstandard" || a.Name == k_SystemPrivateCoreLib);
}
public override AssemblyNameReference ImportReference(AssemblyName reference)
{
return m_CorrectCorlib != null && reference.Name == k_SystemPrivateCoreLib ? m_CorrectCorlib : base.ImportReference(reference);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 484e8ad8c4dde382ea67036b32935ef1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,12 @@
using Mono.Cecil;
namespace MLAPI.Editor.CodeGen
{
internal class PostProcessorReflectionImporterProvider : IReflectionImporterProvider
{
public IReflectionImporter GetReflectionImporter(ModuleDefinition moduleDefinition)
{
return new PostProcessorReflectionImporter(moduleDefinition);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f9273a5dad109ab0783891e36c983080
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,118 @@
#if UNITY_2020_2_OR_NEWER
using System.Collections.Generic;
using System.IO;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Unity.CompilationPipeline.Common.Diagnostics;
using Unity.CompilationPipeline.Common.ILPostProcessing;
using ILPPInterface = Unity.CompilationPipeline.Common.ILPostProcessing.ILPostProcessor;
namespace MLAPI.Editor.CodeGen
{
internal sealed class RuntimeAccessModifiersILPP : ILPPInterface
{
public override ILPPInterface GetInstance() => this;
public override bool WillProcess(ICompiledAssembly compiledAssembly) => compiledAssembly.Name == CodeGenHelpers.RuntimeAssemblyName;
private readonly List<DiagnosticMessage> m_Diagnostics = new List<DiagnosticMessage>();
public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly)
{
if (!WillProcess(compiledAssembly)) return null;
m_Diagnostics.Clear();
// read
var assemblyDefinition = CodeGenHelpers.AssemblyDefinitionFor(compiledAssembly);
if (assemblyDefinition == null)
{
m_Diagnostics.AddError($"Cannot read MLAPI Runtime assembly definition: {compiledAssembly.Name}");
return null;
}
// process
var mainModule = assemblyDefinition.MainModule;
if (mainModule != null)
{
foreach (var typeDefinition in mainModule.Types)
{
if (!typeDefinition.IsClass) continue;
switch (typeDefinition.Name)
{
case nameof(NetworkManager):
ProcessNetworkManager(typeDefinition);
break;
case nameof(NetworkBehaviour):
ProcessNetworkBehaviour(typeDefinition);
break;
case nameof(Messaging.__RpcParams):
typeDefinition.IsPublic = true;
break;
}
}
}
else m_Diagnostics.AddError($"Cannot get main module from MLAPI Runtime assembly definition: {compiledAssembly.Name}");
// write
var pe = new MemoryStream();
var pdb = new MemoryStream();
var writerParameters = new WriterParameters
{
SymbolWriterProvider = new PortablePdbWriterProvider(),
SymbolStream = pdb,
WriteSymbols = true
};
assemblyDefinition.Write(pe, writerParameters);
return new ILPostProcessResult(new InMemoryAssembly(pe.ToArray(), pdb.ToArray()), m_Diagnostics);
}
private void ProcessNetworkManager(TypeDefinition typeDefinition)
{
foreach (var fieldDefinition in typeDefinition.Fields)
{
if (fieldDefinition.Name == nameof(NetworkManager.__ntable))
{
fieldDefinition.IsPublic = true;
}
}
}
private void ProcessNetworkBehaviour(TypeDefinition typeDefinition)
{
foreach (var nestedType in typeDefinition.NestedTypes)
{
if (nestedType.Name == nameof(NetworkBehaviour.__NExec))
{
nestedType.IsNestedFamily = true;
}
}
foreach (var fieldDefinition in typeDefinition.Fields)
{
if (fieldDefinition.Name == nameof(NetworkBehaviour.__nexec))
{
fieldDefinition.IsFamily = true;
}
}
foreach (var methodDefinition in typeDefinition.Methods)
{
switch (methodDefinition.Name)
{
case nameof(NetworkBehaviour.__beginSendServerRpc):
case nameof(NetworkBehaviour.__endSendServerRpc):
case nameof(NetworkBehaviour.__beginSendClientRpc):
case nameof(NetworkBehaviour.__endSendClientRpc):
methodDefinition.IsFamily = true;
break;
}
}
}
}
}
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2c9f2f4b03d774432be69d4c2f53bd2d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,23 @@
{
"name": "Unity.Multiplayer.MLAPI.Editor.CodeGen",
"rootNamespace": "",
"references": [
"Unity.Multiplayer.MLAPI.Runtime"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": true,
"overrideReferences": true,
"precompiledReferences": [
"Mono.Cecil.dll",
"Mono.Cecil.Mdb.dll",
"Mono.Cecil.Pdb.dll",
"Mono.Cecil.Rocks.dll"
],
"autoReferenced": false,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: fe4fa159f4a96442ba22af67ddf20c65
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2c61e8fe9a68a486fbbc3128d233ded2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015, 2016 Sedat Kapanoglu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: cf89ecbf6f9954c8ea6d0848b1e79d87
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,291 @@
// <copyright file="XXHash.cs" company="Sedat Kapanoglu">
// Copyright (c) 2015-2019 Sedat Kapanoglu
// MIT License (see LICENSE file for details)
// </copyright>
// @mfatihmar (Unity): Modified for Unity support
using System.Runtime.CompilerServices;
namespace MLAPI.Editor.CodeGen
{
/// <summary>
/// XXHash implementation.
/// </summary>
internal static class XXHash
{
private const ulong k_Prime64v1 = 11400714785074694791ul;
private const ulong k_Prime64v2 = 14029467366897019727ul;
private const ulong k_Prime64v3 = 1609587929392839161ul;
private const ulong k_Prime64v4 = 9650029242287828579ul;
private const ulong k_Prime64v5 = 2870177450012600261ul;
private const uint k_Prime32v1 = 2654435761u;
private const uint k_Prime32v2 = 2246822519u;
private const uint k_Prime32v3 = 3266489917u;
private const uint k_Prime32v4 = 668265263u;
private const uint k_Prime32v5 = 374761393u;
/// <summary>
/// Generate a 32-bit xxHash value.
/// </summary>
/// <param name="buffer">Input buffer.</param>
/// <param name="bufferLength">Input buffer length.</param>
/// <param name="seed">Optional seed.</param>
/// <returns>32-bit hash value.</returns>
public static unsafe uint Hash32(byte* buffer, int bufferLength, uint seed = 0)
{
const int stripeLength = 16;
int len = bufferLength;
int remainingLen = len;
uint acc;
byte* pInput = buffer;
if (len >= stripeLength)
{
uint acc1 = seed + k_Prime32v1 + k_Prime32v2;
uint acc2 = seed + k_Prime32v2;
uint acc3 = seed;
uint acc4 = seed - k_Prime32v1;
do
{
acc = processStripe32(ref pInput, ref acc1, ref acc2, ref acc3, ref acc4);
remainingLen -= stripeLength;
} while (remainingLen >= stripeLength);
}
else
{
acc = seed + k_Prime32v5;
}
acc += (uint)len;
acc = processRemaining32(pInput, acc, remainingLen);
return avalanche32(acc);
}
/// <summary>
/// Generate a 64-bit xxHash value.
/// </summary>
/// <param name="buffer">Input buffer.</param>
/// <param name="bufferLength">Input buffer length.</param>
/// <param name="seed">Optional seed.</param>
/// <returns>Computed 64-bit hash value.</returns>
public static unsafe ulong Hash64(byte* buffer, int bufferLength, ulong seed = 0)
{
const int stripeLength = 32;
int len = bufferLength;
int remainingLen = len;
ulong acc;
byte* pInput = buffer;
if (len >= stripeLength)
{
ulong acc1 = seed + k_Prime64v1 + k_Prime64v2;
ulong acc2 = seed + k_Prime64v2;
ulong acc3 = seed;
ulong acc4 = seed - k_Prime64v1;
do
{
acc = processStripe64(ref pInput, ref acc1, ref acc2, ref acc3, ref acc4);
remainingLen -= stripeLength;
} while (remainingLen >= stripeLength);
}
else
{
acc = seed + k_Prime64v5;
}
acc += (ulong)len;
acc = processRemaining64(pInput, acc, remainingLen);
return avalanche64(acc);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe ulong processStripe64(
ref byte* pInput,
ref ulong acc1,
ref ulong acc2,
ref ulong acc3,
ref ulong acc4)
{
processLane64(ref acc1, ref pInput);
processLane64(ref acc2, ref pInput);
processLane64(ref acc3, ref pInput);
processLane64(ref acc4, ref pInput);
ulong acc = Bits.RotateLeft(acc1, 1)
+ Bits.RotateLeft(acc2, 7)
+ Bits.RotateLeft(acc3, 12)
+ Bits.RotateLeft(acc4, 18);
mergeAccumulator64(ref acc, acc1);
mergeAccumulator64(ref acc, acc2);
mergeAccumulator64(ref acc, acc3);
mergeAccumulator64(ref acc, acc4);
return acc;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe void processLane64(ref ulong accn, ref byte* pInput)
{
ulong lane = *(ulong*)pInput;
accn = round64(accn, lane);
pInput += 8;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe ulong processRemaining64(
byte* pInput,
ulong acc,
int remainingLen)
{
for (ulong lane; remainingLen >= 8; remainingLen -= 8, pInput += 8)
{
lane = *(ulong*)pInput;
acc ^= round64(0, lane);
acc = Bits.RotateLeft(acc, 27) * k_Prime64v1;
acc += k_Prime64v4;
}
for (uint lane32; remainingLen >= 4; remainingLen -= 4, pInput += 4)
{
lane32 = *(uint*)pInput;
acc ^= lane32 * k_Prime64v1;
acc = Bits.RotateLeft(acc, 23) * k_Prime64v2;
acc += k_Prime64v3;
}
for (byte lane8; remainingLen >= 1; remainingLen--, pInput++)
{
lane8 = *pInput;
acc ^= lane8 * k_Prime64v5;
acc = Bits.RotateLeft(acc, 11) * k_Prime64v1;
}
return acc;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong avalanche64(ulong acc)
{
acc ^= acc >> 33;
acc *= k_Prime64v2;
acc ^= acc >> 29;
acc *= k_Prime64v3;
acc ^= acc >> 32;
return acc;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong round64(ulong accn, ulong lane)
{
accn += lane * k_Prime64v2;
return Bits.RotateLeft(accn, 31) * k_Prime64v1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void mergeAccumulator64(ref ulong acc, ulong accn)
{
acc ^= round64(0, accn);
acc *= k_Prime64v1;
acc += k_Prime64v4;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe uint processStripe32(
ref byte* pInput,
ref uint acc1,
ref uint acc2,
ref uint acc3,
ref uint acc4)
{
processLane32(ref pInput, ref acc1);
processLane32(ref pInput, ref acc2);
processLane32(ref pInput, ref acc3);
processLane32(ref pInput, ref acc4);
return Bits.RotateLeft(acc1, 1)
+ Bits.RotateLeft(acc2, 7)
+ Bits.RotateLeft(acc3, 12)
+ Bits.RotateLeft(acc4, 18);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe void processLane32(ref byte* pInput, ref uint accn)
{
uint lane = *(uint*)pInput;
accn = round32(accn, lane);
pInput += 4;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe uint processRemaining32(
byte* pInput,
uint acc,
int remainingLen)
{
for (uint lane; remainingLen >= 4; remainingLen -= 4, pInput += 4)
{
lane = *(uint*)pInput;
acc += lane * k_Prime32v3;
acc = Bits.RotateLeft(acc, 17) * k_Prime32v4;
}
for (byte lane; remainingLen >= 1; remainingLen--, pInput++)
{
lane = *pInput;
acc += lane * k_Prime32v5;
acc = Bits.RotateLeft(acc, 11) * k_Prime32v1;
}
return acc;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static uint round32(uint accn, uint lane)
{
accn += lane * k_Prime32v2;
accn = Bits.RotateLeft(accn, 13);
accn *= k_Prime32v1;
return accn;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static uint avalanche32(uint acc)
{
acc ^= acc >> 15;
acc *= k_Prime32v2;
acc ^= acc >> 13;
acc *= k_Prime32v3;
acc ^= acc >> 16;
return acc;
}
/// <summary>
/// Bit operations.
/// </summary>
private static class Bits
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static ulong RotateLeft(ulong value, int bits)
{
return (value << bits) | (value >> (64 - bits));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static uint RotateLeft(uint value, int bits)
{
return (value << bits) | (value >> (32 - bits));
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b5aa7a49e9e694f148d810d34577546b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,361 @@
using System.Collections.Generic;
using System.IO;
using MLAPI.Profiling;
using MLAPI.Serialization;
using UnityEngine;
namespace UnityEditor
{
public class MLAPIProfiler : EditorWindow
{
#if !UNITY_2020_2_OR_NEWER
[MenuItem("Window/MLAPI Profiler")]
public static void ShowWindow()
{
GetWindow<MLAPIProfiler>();
}
#endif
private static GUIStyle s_WrapStyle
{
get
{
Color color = EditorStyles.label.normal.textColor;
GUIStyle style = EditorStyles.centeredGreyMiniLabel;
style.wordWrap = true;
style.normal.textColor = color;
return style;
}
}
private float m_HoverAlpha = 0f;
private float m_UpdateDelay = 1f;
private int m_CaptureCount = 100;
private float m_ShowMax = 0;
private float m_ShowMin = 0;
private AnimationCurve m_Curve = AnimationCurve.Linear(0, 0, 1, 0);
private readonly List<ProfilerTick> m_CurrentTicks = new List<ProfilerTick>();
private float m_LastDrawn = 0;
private class ProfilerContainer
{
public ProfilerTick[] Ticks;
public byte[] ToBytes()
{
var buffer = new NetworkBuffer();
var writer = new NetworkWriter(buffer);
writer.WriteUInt16Packed((ushort)Ticks.Length);
for (int i = 0; i < Ticks.Length; i++)
{
Ticks[i].SerializeToStream(buffer);
}
return buffer.ToArray();
}
public static ProfilerContainer FromBytes(byte[] bytes)
{
var container = new ProfilerContainer();
var buffer = new NetworkBuffer(bytes);
var reader = new NetworkReader(buffer);
var count = reader.ReadUInt16Packed();
container.Ticks = new ProfilerTick[count];
for (int i = 0; i < count; i++)
{
container.Ticks[i] = ProfilerTick.FromStream(buffer);
}
return container;
}
}
private void StopRecording()
{
NetworkProfiler.Stop();
}
private void StartRecording()
{
if (NetworkProfiler.IsRunning) StopRecording();
if (NetworkProfiler.Ticks != null && NetworkProfiler.Ticks.Count >= 2)
{
m_Curve = AnimationCurve.Constant(NetworkProfiler.Ticks.ElementAt(0).Frame, NetworkProfiler.Ticks.ElementAt(NetworkProfiler.Ticks.Count - 1).Frame, 0);
}
else
{
m_Curve = AnimationCurve.Constant(0, 1, 0);
}
m_LastDrawn = 0;
NetworkProfiler.Start(m_CaptureCount);
}
private void ClearDrawing()
{
m_CurrentTicks.Clear();
m_Curve = AnimationCurve.Constant(0, 1, 0);
m_LastDrawn = 0;
}
private void ChangeRecordState()
{
if (NetworkProfiler.IsRunning) StopRecording();
else StartRecording();
}
private TickEvent m_EventHover = null;
private double m_LastSetup = 0;
private void OnGUI()
{
bool recording = NetworkProfiler.IsRunning;
float deltaTime = (float)(EditorApplication.timeSinceStartup - m_LastSetup);
m_LastSetup = EditorApplication.timeSinceStartup;
//Draw top bar
EditorGUILayout.BeginVertical();
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button(recording ? "Stop" : "Capture")) ChangeRecordState();
if (GUILayout.Button("Clear")) ClearDrawing();
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.Space();
if (GUILayout.Button("Import datafile"))
{
string path = EditorUtility.OpenFilePanel("Choose a NetworkProfiler file", "", "");
if (!string.IsNullOrEmpty(path))
{
var ticks = ProfilerContainer.FromBytes(File.ReadAllBytes(path)).Ticks;
if (ticks.Length >= 2)
{
m_Curve = AnimationCurve.Constant(ticks[0].EventId, ticks[(ticks.Length - 1)].EventId, 0);
m_ShowMax = ticks.Length;
m_ShowMin = ticks.Length - Mathf.Clamp(100, 0, ticks.Length);
}
else
{
m_Curve = AnimationCurve.Constant(0, 1, 0);
}
m_CurrentTicks.Clear();
for (int i = 0; i < ticks.Length; i++)
{
m_CurrentTicks.Add(ticks[i]);
uint bytes = 0;
if (ticks[i].Events.Count > 0)
{
for (int j = 0; j < ticks[i].Events.Count; j++)
{
var tickEvent = ticks[i].Events[j];
bytes += tickEvent.Bytes;
}
}
m_Curve.AddKey(ticks[i].EventId, bytes);
}
}
}
if (GUILayout.Button("Export datafile"))
{
int max = (int)m_ShowMax;
int min = (int)m_ShowMin;
int ticksInRange = max - min;
var ticks = new ProfilerTick[ticksInRange];
for (int i = min; i < max; i++) ticks[i - min] = m_CurrentTicks[i];
string path = EditorUtility.SaveFilePanel("Save NetworkProfiler data", "", "networkProfilerData", "");
if (!string.IsNullOrEmpty(path)) File.WriteAllBytes(path, new ProfilerContainer { Ticks = ticks }.ToBytes());
}
EditorGUILayout.EndHorizontal();
float prevHis = m_CaptureCount;
m_CaptureCount = EditorGUILayout.DelayedIntField("History count", m_CaptureCount);
if (m_CaptureCount <= 0) m_CaptureCount = 1;
m_UpdateDelay = EditorGUILayout.Slider("Refresh delay", m_UpdateDelay, 0.1f, 10f);
EditorGUILayout.EndVertical();
if (prevHis != m_CaptureCount) StartRecording();
//Cache
if (NetworkProfiler.IsRunning)
{
if (Time.unscaledTime - m_LastDrawn > m_UpdateDelay)
{
m_LastDrawn = Time.unscaledTime;
m_CurrentTicks.Clear();
if (NetworkProfiler.Ticks.Count >= 2)
{
m_Curve = AnimationCurve.Constant(NetworkProfiler.Ticks.ElementAt(0).EventId, NetworkProfiler.Ticks.ElementAt(NetworkProfiler.Ticks.Count - 1).EventId, 0);
}
for (int i = 0; i < NetworkProfiler.Ticks.Count; i++)
{
var tick = NetworkProfiler.Ticks.ElementAt(i);
m_CurrentTicks.Add(tick);
uint bytes = 0;
if (tick.Events.Count > 0)
{
for (int j = 0; j < tick.Events.Count; j++)
{
var tickEvent = tick.Events[j];
bytes += tickEvent.Bytes;
}
}
m_Curve.AddKey(tick.EventId, bytes);
}
}
}
//Draw Animation curve and slider
m_Curve = EditorGUILayout.CurveField(m_Curve);
EditorGUILayout.MinMaxSlider(ref m_ShowMin, ref m_ShowMax, 0, m_CurrentTicks.Count);
//Verify slider values
if (m_ShowMin < 0) m_ShowMin = 0;
if (m_ShowMax > m_CurrentTicks.Count) m_ShowMax = m_CurrentTicks.Count;
if (m_ShowMin <= 0 && m_ShowMax <= 0)
{
m_ShowMin = 0;
m_ShowMax = m_CurrentTicks.Count;
}
//Draw main board
bool hover = false;
int nonEmptyTicks = 0;
int largestTickCount = 0;
int totalTicks = ((int)m_ShowMax - (int)m_ShowMin);
for (int i = (int)m_ShowMin; i < (int)m_ShowMax; i++)
{
if (m_CurrentTicks[i].Events.Count > 0) nonEmptyTicks++; //Count non empty ticks
if (m_CurrentTicks[i].Events.Count > largestTickCount) largestTickCount = m_CurrentTicks[i].Events.Count; //Get how many events the tick with most events has
}
int emptyTicks = totalTicks - nonEmptyTicks;
float equalWidth = position.width / totalTicks;
float propWidth = equalWidth * 0.3f;
float widthPerTick = ((position.width - emptyTicks * propWidth) / nonEmptyTicks);
float currentX = 0;
int emptyStreak = 0;
for (int i = (int)m_ShowMin; i < (int)m_ShowMax; i++)
{
var tick = m_CurrentTicks[i];
if (tick.Events.Count == 0 && i != totalTicks - 1)
{
emptyStreak++;
continue;
}
if (emptyStreak > 0 || i == totalTicks - 1)
{
var dataRect = new Rect(currentX, 140f, propWidth * emptyStreak, position.height - 140f);
currentX += propWidth * emptyStreak;
if (emptyStreak >= 2) EditorGUI.LabelField(new Rect(dataRect.x, dataRect.y, dataRect.width, dataRect.height), emptyStreak.ToString(), s_WrapStyle);
emptyStreak = 0;
}
if (tick.Events.Count > 0)
{
float heightPerEvent = ((position.height - 140f) - (5f * largestTickCount)) / largestTickCount;
float currentY = 140f;
for (int j = 0; j < tick.Events.Count; j++)
{
var tickEvent = tick.Events[j];
var dataRect = new Rect(currentX, currentY, widthPerTick, heightPerEvent);
if (dataRect.Contains(Event.current.mousePosition))
{
hover = true;
m_EventHover = tickEvent;
}
EditorGUI.DrawRect(dataRect, TickTypeToColor(tickEvent.EventType, true));
EditorGUI.LabelField(new Rect(dataRect.x, dataRect.y, dataRect.width, dataRect.height / 2), tickEvent.EventType.ToString(), s_WrapStyle);
EditorGUI.LabelField(new Rect(dataRect.x, dataRect.y + dataRect.height / 2, dataRect.width, dataRect.height / 2), tickEvent.Bytes + "B", s_WrapStyle);
currentY += heightPerEvent + 5f;
}
}
EditorGUI.DrawRect(new Rect(currentX, 100, widthPerTick, 40), TickTypeToColor(tick.Type, false));
EditorGUI.LabelField(new Rect(currentX, 100, widthPerTick, 20), tick.Type.ToString(), s_WrapStyle);
EditorGUI.LabelField(new Rect(currentX, 120, widthPerTick, 20), tick.Frame.ToString(), s_WrapStyle);
currentX += widthPerTick;
}
//Calculate alpha
if (hover)
{
m_HoverAlpha += deltaTime * 10f;
if (m_HoverAlpha > 1f) m_HoverAlpha = 1f;
else if (m_HoverAlpha < 0f) m_HoverAlpha = 0f;
}
else
{
m_HoverAlpha -= deltaTime * 10f;
if (m_HoverAlpha > 1f) m_HoverAlpha = 1f;
else if (m_HoverAlpha < 0f) m_HoverAlpha = 0f;
}
//Draw hover thingy
if (m_EventHover != null)
{
var rect = new Rect(Event.current.mousePosition, new Vector2(500, 100));
EditorGUI.DrawRect(rect, GetEditorColorWithAlpha(m_HoverAlpha));
float heightPerField = (rect.height - 5) / 4;
EditorGUI.LabelField(new Rect(rect.x + 5, rect.y + 5, rect.width, rect.height), "EventType: " + m_EventHover.EventType, GetStyleWithTextAlpha(EditorStyles.label, m_HoverAlpha));
EditorGUI.LabelField(new Rect(rect.x + 5, rect.y + heightPerField * 1 + 5, rect.width, rect.height), "Size: " + m_EventHover.Bytes + "B", GetStyleWithTextAlpha(EditorStyles.label, m_HoverAlpha));
EditorGUI.LabelField(new Rect(rect.x + 5, rect.y + heightPerField * 2 + 5, rect.width, rect.height), "Channel: " + m_EventHover.ChannelName, GetStyleWithTextAlpha(EditorStyles.label, m_HoverAlpha));
EditorGUI.LabelField(new Rect(rect.x + 5, rect.y + heightPerField * 3 + 5, rect.width, rect.height), "MessageType: " + m_EventHover.MessageType, GetStyleWithTextAlpha(EditorStyles.label, m_HoverAlpha));
}
Repaint();
}
private Color TickTypeToColor(TickType type, bool alpha)
{
switch (type)
{
case TickType.Event:
return new Color(0.58f, 0f, 0.56f, alpha ? 0.37f : 0.7f);
case TickType.Receive:
return new Color(0f, 0.85f, 0.85f, alpha ? 0.28f : 0.7f);
case TickType.Send:
return new Color(0, 0.55f, 1f, alpha ? 0.06f : 0.7f);
default:
return Color.clear;
}
}
private Color EditorColor => EditorGUIUtility.isProSkin ? new Color32(56, 56, 56, 255) : new Color32(194, 194, 194, 255);
private Color GetEditorColorWithAlpha(float alpha) => EditorGUIUtility.isProSkin ? new Color(0.22f, 0.22f, 0.22f, alpha) : new Color(0.76f, 0.76f, 0.76f, alpha);
private GUIStyle GetStyleWithTextAlpha(GUIStyle style, float alpha)
{
Color textColor = style.normal.textColor;
textColor.a = alpha;
GUIStyle newStyle = new GUIStyle(style);
newStyle.normal.textColor = textColor;
return newStyle;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 696c1401e8a6b80409a9a02b51cb469c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using MLAPI.Profiling;
using Unity.Profiling;
using UnityEditor;
using UnityEngine;
namespace MLAPI
{
[InitializeOnLoad]
internal static class MLAPIProfilerModule
{
#if UNITY_2020_2_OR_NEWER && ENABLE_PROFILER
private const string k_RpcModuleName = "MLAPI RPCs";
private const string k_OperationModuleName = "MLAPI Operations";
private const string k_MessageModuleName = "MLAPI Messages";
/// <summary>
/// This needs to be in synced with the internal dynamic module structure to provide our own counters
/// </summary>
[Serializable]
private class MLAPIProfilerCounter
{
// Note: These fields are named this way for internal serialization
public string m_Name;
public string m_Category;
}
/// <summary>
/// This needs to be in synced with the internal dynamic module structure to provide our own counters
/// </summary>
[Serializable]
private class MLAPIProfilerModuleData
{
// Note: These fields are named this way for internal serialization
public List<MLAPIProfilerCounter> m_ChartCounters = new List<MLAPIProfilerCounter>();
public List<MLAPIProfilerCounter> m_DetailCounters = new List<MLAPIProfilerCounter>();
public string m_Name;
}
[Serializable]
private class MLAPIModules
{
// Note: These fields are named this way for internal serialization
public List<MLAPIProfilerModuleData> m_Modules;
}
private static List<MLAPIProfilerCounter> CreateRPCCounters() => new List<MLAPIProfilerCounter>()
{
new MLAPIProfilerCounter { m_Name = ProfilerConstants.RpcSent, m_Category = ProfilerCategory.Network.Name },
new MLAPIProfilerCounter { m_Name = ProfilerConstants.RpcReceived, m_Category = ProfilerCategory.Network.Name },
new MLAPIProfilerCounter { m_Name = ProfilerConstants.RpcBatchesSent, m_Category = ProfilerCategory.Network.Name },
new MLAPIProfilerCounter { m_Name = ProfilerConstants.RpcBatchesReceived, m_Category = ProfilerCategory.Network.Name },
new MLAPIProfilerCounter { m_Name = ProfilerConstants.RpcQueueProcessed, m_Category = ProfilerCategory.Network.Name },
new MLAPIProfilerCounter { m_Name = ProfilerConstants.RpcInQueueSize, m_Category = ProfilerCategory.Network.Name },
new MLAPIProfilerCounter { m_Name = ProfilerConstants.RpcOutQueueSize, m_Category = ProfilerCategory.Network.Name },
};
private static List<MLAPIProfilerCounter> CreateOperationsCounters() => new List<MLAPIProfilerCounter>()
{
new MLAPIProfilerCounter { m_Name = ProfilerConstants.Connections, m_Category = ProfilerCategory.Network.Name },
new MLAPIProfilerCounter { m_Name = ProfilerConstants.ReceiveTickRate, m_Category = ProfilerCategory.Network.Name },
};
private static List<MLAPIProfilerCounter> CreateMessagesCounters() => new List<MLAPIProfilerCounter>()
{
new MLAPIProfilerCounter { m_Name = ProfilerConstants.NamedMessageReceived, m_Category = ProfilerCategory.Network.Name },
new MLAPIProfilerCounter { m_Name = ProfilerConstants.UnnamedMessageReceived, m_Category = ProfilerCategory.Network.Name },
new MLAPIProfilerCounter { m_Name = ProfilerConstants.NamedMessageSent, m_Category = ProfilerCategory.Network.Name },
new MLAPIProfilerCounter { m_Name = ProfilerConstants.UnnamedMessageSent, m_Category = ProfilerCategory.Network.Name },
new MLAPIProfilerCounter { m_Name = ProfilerConstants.ByteSent, m_Category = ProfilerCategory.Network.Name },
new MLAPIProfilerCounter { m_Name = ProfilerConstants.ByteReceived, m_Category = ProfilerCategory.Network.Name },
new MLAPIProfilerCounter { m_Name = ProfilerConstants.NetworkVarUpdates, m_Category = ProfilerCategory.Network.Name },
new MLAPIProfilerCounter { m_Name = ProfilerConstants.NetworkVarDeltas, m_Category = ProfilerCategory.Network.Name },
};
private delegate List<MLAPIProfilerCounter> CounterListFactoryDelegate();
private static bool CreateMLAPIDynamicModule(ref MLAPIModules mlapiModules, string moduleName, CounterListFactoryDelegate counterListFactoryDelegate)
{
var module = mlapiModules.m_Modules.Find(x => x.m_Name == moduleName);
if (module == null)
{
var newModule = new MLAPIProfilerModuleData
{
m_Name = moduleName, m_ChartCounters = counterListFactoryDelegate(), m_DetailCounters = counterListFactoryDelegate(),
};
mlapiModules.m_Modules.Add(newModule);
return true;
}
return false;
}
#endif
static MLAPIProfilerModule()
{
#if UNITY_2020_2_OR_NEWER && ENABLE_PROFILER
var dynamicModulesJson = EditorPrefs.GetString("ProfilerWindow.DynamicModules");
var dynamicModules = JsonUtility.FromJson<MLAPIModules>(dynamicModulesJson);
if (dynamicModules != null)
{
bool wasCreated = CreateMLAPIDynamicModule(ref dynamicModules, k_RpcModuleName, CreateRPCCounters);
wasCreated |= CreateMLAPIDynamicModule(ref dynamicModules, k_OperationModuleName, CreateOperationsCounters);
wasCreated |= CreateMLAPIDynamicModule(ref dynamicModules, k_MessageModuleName, CreateMessagesCounters);
if (wasCreated)
{
EditorPrefs.SetString("ProfilerWindow.DynamicModules", JsonUtility.ToJson(dynamicModules));
}
}
#endif
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ff21a65efe3f4150b3cba2842f170d7d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,78 @@
using System;
using MLAPI.Prototyping;
using UnityEditor.Animations;
using UnityEngine;
namespace UnityEditor
{
[CustomEditor(typeof(NetworkAnimator), true)]
[CanEditMultipleObjects]
public class NetworkAnimatorEditor : Editor
{
private NetworkAnimator m_Target;
[NonSerialized]
private bool m_Initialized;
private SerializedProperty m_AnimatorProperty;
private GUIContent m_AnimatorLabel;
private void Initialize()
{
if (m_Initialized) return;
m_Initialized = true;
m_Target = target as NetworkAnimator;
m_AnimatorProperty = serializedObject.FindProperty("m_Animator");
m_AnimatorLabel = new GUIContent("Animator", "The Animator component to synchronize.");
}
private void DrawControls()
{
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_AnimatorProperty, m_AnimatorLabel);
if (EditorGUI.EndChangeCheck()) m_Target.ResetTrackedParams();
var animator = m_Target.Animator;
if (animator == null) return;
var animatorController = animator.runtimeAnimatorController as AnimatorController;
if (animatorController == null) return;
EditorGUI.indentLevel += 1;
var showWarning = false;
{
int paramIndex = 0;
foreach (var animParam in animatorController.parameters)
{
if (paramIndex >= 32)
{
showWarning = true;
break;
}
bool wasTracking = m_Target.GetParamTracking(paramIndex);
bool isTracking = EditorGUILayout.Toggle(animParam.name, wasTracking);
if (isTracking != wasTracking)
{
m_Target.SetParamTracking(paramIndex, isTracking);
EditorUtility.SetDirty(target);
}
paramIndex++;
}
}
if (showWarning) EditorGUILayout.HelpBox("NetworkAnimator can only select between the first 32 parameters in a mecanim controller", MessageType.Warning);
EditorGUI.indentLevel -= 1;
}
public override void OnInspectorGUI()
{
Initialize();
serializedObject.Update();
DrawControls();
serializedObject.ApplyModifiedProperties();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 26e973580f9aacc46863bde0a99665e6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,164 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using MLAPI;
using MLAPI.NetworkVariable;
using UnityEngine;
namespace UnityEditor
{
[CustomEditor(typeof(NetworkBehaviour), true)]
[CanEditMultipleObjects]
public class NetworkBehaviourEditor : Editor
{
private bool m_Initialized;
private readonly List<string> m_NetworkVariableNames = new List<string>();
private readonly Dictionary<string, FieldInfo> m_NetworkVariableFields = new Dictionary<string, FieldInfo>();
private readonly Dictionary<string, object> m_NetworkVariableObjects = new Dictionary<string, object>();
private GUIContent m_NetworkVariableLabelGuiContent;
private void Init(MonoScript script)
{
m_Initialized = true;
m_NetworkVariableNames.Clear();
m_NetworkVariableFields.Clear();
m_NetworkVariableObjects.Clear();
m_NetworkVariableLabelGuiContent = new GUIContent("NetworkVariable", "This variable is a NetworkVariable. It can not be serialized and can only be changed during runtime.");
var fields = script.GetClass().GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
for (int i = 0; i < fields.Length; i++)
{
var ft = fields[i].FieldType;
if (ft.IsGenericType && ft.GetGenericTypeDefinition() == typeof(NetworkVariable<>) && !fields[i].IsDefined(typeof(HideInInspector), true))
{
m_NetworkVariableNames.Add(fields[i].Name);
m_NetworkVariableFields.Add(fields[i].Name, fields[i]);
}
}
}
private void RenderNetworkVariable(int index)
{
if (!m_NetworkVariableFields.ContainsKey(m_NetworkVariableNames[index]))
{
serializedObject.Update();
var scriptProperty = serializedObject.FindProperty("m_Script");
if (scriptProperty == null) return;
var targetScript = scriptProperty.objectReferenceValue as MonoScript;
Init(targetScript);
}
object value = m_NetworkVariableFields[m_NetworkVariableNames[index]].GetValue(target);
if (value == null)
{
var fieldType = m_NetworkVariableFields[m_NetworkVariableNames[index]].FieldType;
var networkVariable = (INetworkVariable)Activator.CreateInstance(fieldType, true);
m_NetworkVariableFields[m_NetworkVariableNames[index]].SetValue(target, networkVariable);
}
var type = m_NetworkVariableFields[m_NetworkVariableNames[index]].GetValue(target).GetType();
var genericType = type.GetGenericArguments()[0];
EditorGUILayout.BeginHorizontal();
if (genericType == typeof(string))
{
var networkVariable = (NetworkVariable<string>)m_NetworkVariableFields[m_NetworkVariableNames[index]].GetValue(target);
networkVariable.Value = EditorGUILayout.TextField(m_NetworkVariableNames[index], networkVariable.Value);
}
else if (genericType.IsValueType)
{
var method = typeof(NetworkBehaviourEditor).GetMethod("RenderNetworkVariableValueType", BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic);
var genericMethod = method.MakeGenericMethod(genericType);
genericMethod.Invoke(this, new[] { (object)index });
}
else
{
EditorGUILayout.LabelField("Type not renderable");
}
GUILayout.Label(m_NetworkVariableLabelGuiContent, EditorStyles.miniLabel, GUILayout.Width(EditorStyles.miniLabel.CalcSize(m_NetworkVariableLabelGuiContent).x));
EditorGUILayout.EndHorizontal();
}
private void RenderNetworkVariableValueType<T>(int index) where T : struct
{
var networkVariable = (NetworkVariable<T>)m_NetworkVariableFields[m_NetworkVariableNames[index]].GetValue(target);
var type = typeof(T);
object val = networkVariable.Value;
string name = m_NetworkVariableNames[index];
if (NetworkManager.Singleton != null && NetworkManager.Singleton.IsListening)
{
if (type == typeof(int)) val = EditorGUILayout.IntField(name, (int)val);
else if (type == typeof(uint)) val = (uint)EditorGUILayout.LongField(name, (long)((uint)val));
else if (type == typeof(short)) val = (short)EditorGUILayout.IntField(name, (int)((short)val));
else if (type == typeof(ushort)) val = (ushort)EditorGUILayout.IntField(name, (int)((ushort)val));
else if (type == typeof(sbyte)) val = (sbyte)EditorGUILayout.IntField(name, (int)((sbyte)val));
else if (type == typeof(byte)) val = (byte)EditorGUILayout.IntField(name, (int)((byte)val));
else if (type == typeof(long)) val = EditorGUILayout.LongField(name, (long)val);
else if (type == typeof(ulong)) val = (ulong)EditorGUILayout.LongField(name, (long)((ulong)val));
else if (type == typeof(bool)) val = EditorGUILayout.Toggle(name, (bool)val);
else if (type == typeof(string)) val = EditorGUILayout.TextField(name, (string)val);
else if (type.IsEnum) val = EditorGUILayout.EnumPopup(name, (Enum)val);
else EditorGUILayout.LabelField("Type not renderable");
networkVariable.Value = (T)val;
}
else
{
EditorGUILayout.LabelField(name, EditorStyles.wordWrappedLabel);
EditorGUILayout.SelectableLabel(val.ToString(), EditorStyles.wordWrappedLabel);
}
}
public override void OnInspectorGUI()
{
if (!m_Initialized)
{
serializedObject.Update();
var scriptProperty = serializedObject.FindProperty("m_Script");
if (scriptProperty == null) return;
var targetScript = scriptProperty.objectReferenceValue as MonoScript;
Init(targetScript);
}
EditorGUI.BeginChangeCheck();
serializedObject.Update();
for (int i = 0; i < m_NetworkVariableNames.Count; i++)
{
RenderNetworkVariable(i);
}
var property = serializedObject.GetIterator();
bool expanded = true;
while (property.NextVisible(expanded))
{
if (property.propertyType == SerializedPropertyType.ObjectReference)
{
if (property.name == "m_Script") EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.PropertyField(property, true);
if (property.name == "m_Script") EditorGUI.EndDisabledGroup();
}
else
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(property, true);
EditorGUILayout.EndHorizontal();
}
expanded = false;
}
serializedObject.ApplyModifiedProperties();
EditorGUI.EndChangeCheck();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a4a5c9c08a4038e449fd259764bc663f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,408 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using UnityEditorInternal;
using MLAPI;
using MLAPI.Transports;
[CustomEditor(typeof(NetworkManager), true)]
[CanEditMultipleObjects]
public class NetworkManagerEditor : Editor
{
// Properties
private SerializedProperty m_DontDestroyOnLoadProperty;
private SerializedProperty m_RunInBackgroundProperty;
private SerializedProperty m_LogLevelProperty;
// NetworkConfig
private SerializedProperty m_NetworkConfigProperty;
// NetworkConfig fields
private SerializedProperty m_ProtocolVersionProperty;
private SerializedProperty m_AllowRuntimeSceneChangesProperty;
private SerializedProperty m_NetworkTransportProperty;
private SerializedProperty m_ReceiveTickrateProperty;
private SerializedProperty m_NetworkTickIntervalSecProperty;
private SerializedProperty m_MaxReceiveEventsPerTickRateProperty;
private SerializedProperty m_EventTickrateProperty;
private SerializedProperty m_MaxObjectUpdatesPerTickProperty;
private SerializedProperty m_ClientConnectionBufferTimeoutProperty;
private SerializedProperty m_ConnectionApprovalProperty;
private SerializedProperty m_EnableTimeResyncProperty;
private SerializedProperty m_TimeResyncIntervalProperty;
private SerializedProperty m_EnableNetworkVariableProperty;
private SerializedProperty m_EnsureNetworkVariableLengthSafetyProperty;
private SerializedProperty m_CreatePlayerPrefabProperty;
private SerializedProperty m_ForceSamePrefabsProperty;
private SerializedProperty m_UsePrefabSyncProperty;
private SerializedProperty m_EnableSceneManagementProperty;
private SerializedProperty m_RecycleNetworkIdsProperty;
private SerializedProperty m_NetworkIdRecycleDelayProperty;
private SerializedProperty m_RpcHashSizeProperty;
private SerializedProperty m_LoadSceneTimeOutProperty;
private SerializedProperty m_EnableMessageBufferingProperty;
private SerializedProperty m_MessageBufferTimeoutProperty;
private ReorderableList m_NetworkPrefabsList;
private ReorderableList m_RegisteredScenesList;
private NetworkManager m_NetworkManager;
private bool m_Initialized;
private readonly List<Type> m_TransportTypes = new List<Type>();
private string[] m_TransportNames = { "Select transport..." };
private void ReloadTransports()
{
m_TransportTypes.Clear();
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
var types = assembly.GetTypes();
foreach (var type in types)
{
if (type.IsSubclassOf(typeof(NetworkTransport)))
{
m_TransportTypes.Add(type);
}
}
}
m_TransportNames = new string[m_TransportTypes.Count + 1];
m_TransportNames[0] = "Select transport...";
for (int i = 0; i < m_TransportTypes.Count; i++)
{
m_TransportNames[i + 1] = m_TransportTypes[i].Name;
}
}
private void Init()
{
if (m_Initialized) return;
m_Initialized = true;
m_NetworkManager = (NetworkManager)target;
// Base properties
m_DontDestroyOnLoadProperty = serializedObject.FindProperty("DontDestroy");
m_RunInBackgroundProperty = serializedObject.FindProperty("RunInBackground");
m_LogLevelProperty = serializedObject.FindProperty("LogLevel");
m_NetworkConfigProperty = serializedObject.FindProperty("NetworkConfig");
// NetworkConfig properties
m_ProtocolVersionProperty = m_NetworkConfigProperty.FindPropertyRelative("ProtocolVersion");
m_AllowRuntimeSceneChangesProperty = m_NetworkConfigProperty.FindPropertyRelative("AllowRuntimeSceneChanges");
m_NetworkTransportProperty = m_NetworkConfigProperty.FindPropertyRelative("NetworkTransport");
m_ReceiveTickrateProperty = m_NetworkConfigProperty.FindPropertyRelative("ReceiveTickrate");
m_NetworkTickIntervalSecProperty = m_NetworkConfigProperty.FindPropertyRelative("NetworkTickIntervalSec");
m_MaxReceiveEventsPerTickRateProperty = m_NetworkConfigProperty.FindPropertyRelative("MaxReceiveEventsPerTickRate");
m_EventTickrateProperty = m_NetworkConfigProperty.FindPropertyRelative("EventTickrate");
m_ClientConnectionBufferTimeoutProperty = m_NetworkConfigProperty.FindPropertyRelative("ClientConnectionBufferTimeout");
m_ConnectionApprovalProperty = m_NetworkConfigProperty.FindPropertyRelative("ConnectionApproval");
m_EnableTimeResyncProperty = m_NetworkConfigProperty.FindPropertyRelative("EnableTimeResync");
m_TimeResyncIntervalProperty = m_NetworkConfigProperty.FindPropertyRelative("TimeResyncInterval");
m_EnableNetworkVariableProperty = m_NetworkConfigProperty.FindPropertyRelative("EnableNetworkVariable");
m_EnsureNetworkVariableLengthSafetyProperty = m_NetworkConfigProperty.FindPropertyRelative("EnsureNetworkVariableLengthSafety");
m_CreatePlayerPrefabProperty = m_NetworkConfigProperty.FindPropertyRelative("CreatePlayerPrefab");
m_ForceSamePrefabsProperty = m_NetworkConfigProperty.FindPropertyRelative("ForceSamePrefabs");
m_UsePrefabSyncProperty = m_NetworkConfigProperty.FindPropertyRelative("UsePrefabSync");
m_EnableSceneManagementProperty = m_NetworkConfigProperty.FindPropertyRelative("EnableSceneManagement");
m_RecycleNetworkIdsProperty = m_NetworkConfigProperty.FindPropertyRelative("RecycleNetworkIds");
m_NetworkIdRecycleDelayProperty = m_NetworkConfigProperty.FindPropertyRelative("NetworkIdRecycleDelay");
m_RpcHashSizeProperty = m_NetworkConfigProperty.FindPropertyRelative("RpcHashSize");
m_LoadSceneTimeOutProperty = m_NetworkConfigProperty.FindPropertyRelative("LoadSceneTimeOut");
m_EnableMessageBufferingProperty = m_NetworkConfigProperty.FindPropertyRelative("EnableMessageBuffering");
m_MessageBufferTimeoutProperty = m_NetworkConfigProperty.FindPropertyRelative("MessageBufferTimeout");
ReloadTransports();
}
private void CheckNullProperties()
{
// Base properties
m_DontDestroyOnLoadProperty = serializedObject.FindProperty("DontDestroy");
m_RunInBackgroundProperty = serializedObject.FindProperty("RunInBackground");
m_LogLevelProperty = serializedObject.FindProperty("LogLevel");
m_NetworkConfigProperty = serializedObject.FindProperty("NetworkConfig");
// NetworkConfig properties
m_ProtocolVersionProperty = m_NetworkConfigProperty.FindPropertyRelative("ProtocolVersion");
m_AllowRuntimeSceneChangesProperty = m_NetworkConfigProperty.FindPropertyRelative("AllowRuntimeSceneChanges");
m_NetworkTransportProperty = m_NetworkConfigProperty.FindPropertyRelative("NetworkTransport");
m_ReceiveTickrateProperty = m_NetworkConfigProperty.FindPropertyRelative("ReceiveTickrate");
m_NetworkTickIntervalSecProperty = m_NetworkConfigProperty.FindPropertyRelative("NetworkTickIntervalSec");
m_MaxReceiveEventsPerTickRateProperty = m_NetworkConfigProperty.FindPropertyRelative("MaxReceiveEventsPerTickRate");
m_EventTickrateProperty = m_NetworkConfigProperty.FindPropertyRelative("EventTickrate");
m_ClientConnectionBufferTimeoutProperty = m_NetworkConfigProperty.FindPropertyRelative("ClientConnectionBufferTimeout");
m_ConnectionApprovalProperty = m_NetworkConfigProperty.FindPropertyRelative("ConnectionApproval");
m_EnableTimeResyncProperty = m_NetworkConfigProperty.FindPropertyRelative("EnableTimeResync");
m_TimeResyncIntervalProperty = m_NetworkConfigProperty.FindPropertyRelative("TimeResyncInterval");
m_EnableNetworkVariableProperty = m_NetworkConfigProperty.FindPropertyRelative("EnableNetworkVariable");
m_EnsureNetworkVariableLengthSafetyProperty = m_NetworkConfigProperty.FindPropertyRelative("EnsureNetworkVariableLengthSafety");
m_CreatePlayerPrefabProperty = m_NetworkConfigProperty.FindPropertyRelative("CreatePlayerPrefab");
m_ForceSamePrefabsProperty = m_NetworkConfigProperty.FindPropertyRelative("ForceSamePrefabs");
m_UsePrefabSyncProperty = m_NetworkConfigProperty.FindPropertyRelative("UsePrefabSync");
m_EnableSceneManagementProperty = m_NetworkConfigProperty.FindPropertyRelative("EnableSceneManagement");
m_RecycleNetworkIdsProperty = m_NetworkConfigProperty.FindPropertyRelative("RecycleNetworkIds");
m_NetworkIdRecycleDelayProperty = m_NetworkConfigProperty.FindPropertyRelative("NetworkIdRecycleDelay");
m_RpcHashSizeProperty = m_NetworkConfigProperty.FindPropertyRelative("RpcHashSize");
m_LoadSceneTimeOutProperty = m_NetworkConfigProperty.FindPropertyRelative("LoadSceneTimeOut");
m_EnableMessageBufferingProperty = m_NetworkConfigProperty.FindPropertyRelative("EnableMessageBuffering");
m_MessageBufferTimeoutProperty = m_NetworkConfigProperty.FindPropertyRelative("MessageBufferTimeout");
}
private void OnEnable()
{
m_NetworkPrefabsList = new ReorderableList(serializedObject, serializedObject.FindProperty("NetworkConfig").FindPropertyRelative("NetworkPrefabs"), true, true, true, true);
m_NetworkPrefabsList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
{
var element = m_NetworkPrefabsList.serializedProperty.GetArrayElementAtIndex(index);
int firstLabelWidth = 50;
int secondLabelWidth = 140;
float secondFieldWidth = 10;
int reduceFirstWidth = 45;
EditorGUI.LabelField(new Rect(rect.x, rect.y, firstLabelWidth, EditorGUIUtility.singleLineHeight), "Prefab");
EditorGUI.PropertyField(new Rect(rect.x + firstLabelWidth, rect.y, rect.width - firstLabelWidth - secondLabelWidth - secondFieldWidth - reduceFirstWidth,
EditorGUIUtility.singleLineHeight), element.FindPropertyRelative("Prefab"), GUIContent.none);
EditorGUI.LabelField(new Rect(rect.width - secondLabelWidth - secondFieldWidth, rect.y, secondLabelWidth, EditorGUIUtility.singleLineHeight), "Default Player Prefab");
int playerPrefabIndex = -1;
for (int i = 0; i < m_NetworkManager.NetworkConfig.NetworkPrefabs.Count; i++)
{
if (m_NetworkManager.NetworkConfig.NetworkPrefabs[i].PlayerPrefab)
{
playerPrefabIndex = i;
break;
}
}
using (new EditorGUI.DisabledScope(playerPrefabIndex != -1 && playerPrefabIndex != index))
{
EditorGUI.PropertyField(new Rect(rect.width - secondFieldWidth, rect.y, secondFieldWidth,
EditorGUIUtility.singleLineHeight), element.FindPropertyRelative("PlayerPrefab"), GUIContent.none);
}
};
m_NetworkPrefabsList.drawHeaderCallback = (Rect rect) => { EditorGUI.LabelField(rect, "NetworkPrefabs"); };
m_RegisteredScenesList = new ReorderableList(serializedObject, serializedObject.FindProperty("NetworkConfig").FindPropertyRelative("RegisteredScenes"), true, true, true, true);
m_RegisteredScenesList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
{
var element = m_RegisteredScenesList.serializedProperty.GetArrayElementAtIndex(index);
int firstLabelWidth = 50;
int padding = 20;
EditorGUI.LabelField(new Rect(rect.x, rect.y, firstLabelWidth, EditorGUIUtility.singleLineHeight), "Name");
EditorGUI.PropertyField(new Rect(rect.x + firstLabelWidth, rect.y, rect.width - firstLabelWidth - padding,
EditorGUIUtility.singleLineHeight), element, GUIContent.none);
};
m_RegisteredScenesList.drawHeaderCallback = (Rect rect) => { EditorGUI.LabelField(rect, "Registered Scene Names"); };
}
public override void OnInspectorGUI()
{
Init();
CheckNullProperties();
{
var iterator = serializedObject.GetIterator();
for (bool enterChildren = true; iterator.NextVisible(enterChildren); enterChildren = false)
{
using (new EditorGUI.DisabledScope("m_Script" == iterator.propertyPath))
{
EditorGUILayout.PropertyField(iterator, false);
}
}
}
if (!m_NetworkManager.IsServer && !m_NetworkManager.IsClient)
{
serializedObject.Update();
EditorGUILayout.PropertyField(m_DontDestroyOnLoadProperty);
EditorGUILayout.PropertyField(m_RunInBackgroundProperty);
EditorGUILayout.PropertyField(m_LogLevelProperty);
EditorGUILayout.Space();
m_NetworkPrefabsList.DoLayoutList();
using (new EditorGUI.DisabledScope(!m_NetworkManager.NetworkConfig.EnableSceneManagement))
{
m_RegisteredScenesList.DoLayoutList();
EditorGUILayout.Space();
}
EditorGUILayout.LabelField("General", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(m_ProtocolVersionProperty);
EditorGUILayout.PropertyField(m_NetworkTransportProperty);
if (m_NetworkTransportProperty.objectReferenceValue == null)
{
EditorGUILayout.HelpBox("You have no transport selected. A transport is required for the MLAPI to work. Which one do you want?", MessageType.Warning);
int selection = EditorGUILayout.Popup(0, m_TransportNames);
if (selection > 0)
{
ReloadTransports();
var transportComponent = m_NetworkManager.gameObject.GetComponent(m_TransportTypes[selection - 1]);
if (transportComponent == null)
{
transportComponent = m_NetworkManager.gameObject.AddComponent(m_TransportTypes[selection - 1]);
}
m_NetworkTransportProperty.objectReferenceValue = transportComponent;
Repaint();
}
}
EditorGUILayout.PropertyField(m_EnableTimeResyncProperty);
using (new EditorGUI.DisabledScope(!m_NetworkManager.NetworkConfig.EnableTimeResync))
{
EditorGUILayout.PropertyField(m_TimeResyncIntervalProperty);
}
EditorGUILayout.LabelField("Performance", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(m_ReceiveTickrateProperty);
EditorGUILayout.PropertyField(m_NetworkTickIntervalSecProperty);
EditorGUILayout.PropertyField(m_MaxReceiveEventsPerTickRateProperty);
EditorGUILayout.PropertyField(m_EventTickrateProperty);
EditorGUILayout.PropertyField(m_EnableNetworkVariableProperty);
using (new EditorGUI.DisabledScope(!m_NetworkManager.NetworkConfig.EnableNetworkVariable))
{
if (m_MaxObjectUpdatesPerTickProperty != null)
{
EditorGUILayout.PropertyField(m_MaxObjectUpdatesPerTickProperty);
}
EditorGUILayout.PropertyField(m_EnsureNetworkVariableLengthSafetyProperty);
}
EditorGUILayout.LabelField("Connection", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(m_ConnectionApprovalProperty);
using (new EditorGUI.DisabledScope(!m_NetworkManager.NetworkConfig.ConnectionApproval))
{
EditorGUILayout.PropertyField(m_ClientConnectionBufferTimeoutProperty);
}
EditorGUILayout.LabelField("Spawning", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(m_CreatePlayerPrefabProperty);
EditorGUILayout.PropertyField(m_ForceSamePrefabsProperty);
using (new EditorGUI.DisabledScope(!m_NetworkManager.NetworkConfig.EnableSceneManagement))
{
bool value = m_NetworkManager.NetworkConfig.UsePrefabSync;
if (!m_NetworkManager.NetworkConfig.EnableSceneManagement)
{
m_UsePrefabSyncProperty.boolValue = true;
}
EditorGUILayout.PropertyField(m_UsePrefabSyncProperty);
if (!m_NetworkManager.NetworkConfig.EnableSceneManagement)
{
m_UsePrefabSyncProperty.boolValue = value;
}
}
EditorGUILayout.PropertyField(m_RecycleNetworkIdsProperty);
using (new EditorGUI.DisabledScope(!m_NetworkManager.NetworkConfig.RecycleNetworkIds))
{
EditorGUILayout.PropertyField(m_NetworkIdRecycleDelayProperty);
}
EditorGUILayout.PropertyField(m_EnableMessageBufferingProperty);
using (new EditorGUI.DisabledScope(!m_NetworkManager.NetworkConfig.EnableMessageBuffering))
{
EditorGUILayout.PropertyField(m_MessageBufferTimeoutProperty);
}
EditorGUILayout.LabelField("Bandwidth", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(m_RpcHashSizeProperty);
EditorGUILayout.LabelField("Scene Management", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(m_EnableSceneManagementProperty);
using (new EditorGUI.DisabledScope(!m_NetworkManager.NetworkConfig.EnableSceneManagement))
{
EditorGUILayout.PropertyField(m_LoadSceneTimeOutProperty);
EditorGUILayout.PropertyField(m_AllowRuntimeSceneChangesProperty);
}
serializedObject.ApplyModifiedProperties();
// Start buttons below
{
string buttonDisabledReasonSuffix = "";
if (!EditorApplication.isPlaying)
{
buttonDisabledReasonSuffix = ". This can only be done in play mode";
GUI.enabled = false;
}
if (GUILayout.Button(new GUIContent("Start Host", "Starts a host instance" + buttonDisabledReasonSuffix)))
{
m_NetworkManager.StartHost();
}
if (GUILayout.Button(new GUIContent("Start Server", "Starts a server instance" + buttonDisabledReasonSuffix)))
{
m_NetworkManager.StartServer();
}
if (GUILayout.Button(new GUIContent("Start Client", "Starts a client instance" + buttonDisabledReasonSuffix)))
{
m_NetworkManager.StartClient();
}
if (!EditorApplication.isPlaying)
{
GUI.enabled = true;
}
}
}
else
{
string instanceType = string.Empty;
if (m_NetworkManager.IsHost) instanceType = "Host";
else if (m_NetworkManager.IsServer) instanceType = "Server";
else if (m_NetworkManager.IsClient) instanceType = "Client";
EditorGUILayout.HelpBox("You cannot edit the NetworkConfig when a " + instanceType + " is running.", MessageType.Info);
if (GUILayout.Button(new GUIContent("Stop " + instanceType, "Stops the " + instanceType + " instance.")))
{
if (m_NetworkManager.IsHost) m_NetworkManager.StopHost();
else if (m_NetworkManager.IsServer) m_NetworkManager.StopServer();
else if (m_NetworkManager.IsClient) m_NetworkManager.StopClient();
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 74a8f011a324b7642b69098fe57bf635
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,87 @@
using System.Collections.Generic;
using MLAPI;
using UnityEngine;
namespace UnityEditor
{
[CustomEditor(typeof(NetworkObject), true)]
[CanEditMultipleObjects]
public class NetworkObjectEditor : Editor
{
private bool m_Initialized;
private NetworkObject m_NetworkObject;
private bool m_ShowObservers;
private void Init()
{
if (m_Initialized) return;
m_Initialized = true;
m_NetworkObject = (NetworkObject)target;
}
public override void OnInspectorGUI()
{
Init();
if (!m_NetworkObject.IsSpawned && NetworkManager.Singleton != null && NetworkManager.Singleton.IsServer)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(new GUIContent("Spawn", "Spawns the object across the network"));
if (GUILayout.Toggle(false, "Spawn", EditorStyles.miniButtonLeft))
{
m_NetworkObject.Spawn();
EditorUtility.SetDirty(target);
}
EditorGUILayout.EndHorizontal();
}
else if (m_NetworkObject.IsSpawned)
{
EditorGUILayout.LabelField("PrefabHashGenerator: ", m_NetworkObject.PrefabHashGenerator, EditorStyles.label);
EditorGUILayout.LabelField("PrefabHash: ", m_NetworkObject.PrefabHash.ToString(), EditorStyles.label);
EditorGUILayout.LabelField("InstanceId: ", m_NetworkObject.NetworkInstanceId.ToString(), EditorStyles.label);
EditorGUILayout.LabelField("NetworkId: ", m_NetworkObject.NetworkObjectId.ToString(), EditorStyles.label);
EditorGUILayout.LabelField("OwnerId: ", m_NetworkObject.OwnerClientId.ToString(), EditorStyles.label);
EditorGUILayout.LabelField("IsSpawned: ", m_NetworkObject.IsSpawned.ToString(), EditorStyles.label);
EditorGUILayout.LabelField("IsLocalPlayer: ", m_NetworkObject.IsLocalPlayer.ToString(), EditorStyles.label);
EditorGUILayout.LabelField("IsOwner: ", m_NetworkObject.IsOwner.ToString(), EditorStyles.label);
EditorGUILayout.LabelField("IsOwnedByServer: ", m_NetworkObject.IsOwnedByServer.ToString(), EditorStyles.label);
EditorGUILayout.LabelField("IsPlayerObject: ", m_NetworkObject.IsPlayerObject.ToString(), EditorStyles.label);
EditorGUILayout.LabelField("IsSceneObject: ", (m_NetworkObject.IsSceneObject == null ? "Null" : m_NetworkObject.IsSceneObject.Value.ToString()), EditorStyles.label);
if (NetworkManager.Singleton != null && NetworkManager.Singleton.IsServer)
{
m_ShowObservers = EditorGUILayout.Foldout(m_ShowObservers, "Observers");
if (m_ShowObservers)
{
HashSet<ulong>.Enumerator observerClientIds = m_NetworkObject.GetObservers();
EditorGUI.indentLevel += 1;
while (observerClientIds.MoveNext())
{
if (NetworkManager.Singleton.ConnectedClients[observerClientIds.Current].PlayerObject != null)
{
EditorGUILayout.ObjectField("ClientId: " + observerClientIds.Current, NetworkManager.Singleton.ConnectedClients[observerClientIds.Current].PlayerObject, typeof(GameObject), false);
}
else
{
EditorGUILayout.TextField("ClientId: " + observerClientIds.Current, EditorStyles.label);
}
}
EditorGUI.indentLevel -= 1;
}
}
}
else
{
base.OnInspectorGUI();
EditorGUILayout.LabelField("PrefabHash: ", m_NetworkObject.PrefabHash.ToString(), EditorStyles.label);
EditorGUILayout.LabelField("InstanceId: ", m_NetworkObject.NetworkInstanceId.ToString(), EditorStyles.label);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 36e4b519d287d0f4e8bfb7d088a9275f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,67 @@
using System.Collections.Generic;
using System.Linq;
using MLAPI;
using UnityEditor.Callbacks;
using UnityEngine;
namespace UnityEditor
{
public class NetworkScenePostProcess : MonoBehaviour
{
[PostProcessScene(int.MaxValue)]
public static void ProcessScene()
{
// find all scene objects that have not been spawned yet
// TODO: long term, replace the means of finding candidate objects to repace FindObjectsOfType
var traverseSortedObjects = FindObjectsOfType<NetworkObject>().Where(x => x.IsSceneObject == null).ToList();
traverseSortedObjects.Sort((x, y) =>
{
List<int> xSiblingIndex = x.TraversedSiblingIndex();
List<int> ySiblingIndex = y.TraversedSiblingIndex();
while (xSiblingIndex.Count > 0 && ySiblingIndex.Count > 0)
{
if (xSiblingIndex[0] < ySiblingIndex[0])
{
return -1;
}
if (xSiblingIndex[0] > ySiblingIndex[0])
{
return 1;
}
xSiblingIndex.RemoveAt(0);
ySiblingIndex.RemoveAt(0);
}
return 0;
});
for (ulong i = 0; i < (ulong)traverseSortedObjects.Count; i++)
{
traverseSortedObjects[(int)i].NetworkInstanceId = i;
}
}
}
internal static class PrefabHelpers
{
internal static List<int> TraversedSiblingIndex(this NetworkObject networkObject)
{
var paths = new List<int>();
var transform = networkObject.transform;
while (transform != null)
{
paths.Add(transform.GetSiblingIndex());
transform = transform.parent;
}
paths.Reverse();
return paths;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b8ffd6f352aba584ba0835e2f4221caa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,19 @@
{
"name": "Unity.Multiplayer.MLAPI.Editor",
"rootNamespace": "",
"references": [
"Unity.Multiplayer.MLAPI.Runtime",
"Unity.Multiplayer.MLAPI.Prototyping"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": false,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 9f4f5bf029cebb64f983b7bdc29f62a1
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: