Initial Commit
This commit is contained in:
parent
53eb92e9af
commit
270ab7d11f
15341 changed files with 700234 additions and 0 deletions
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bbb4974b4302f435b9f4663c64d8f803
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0e5541b3bca0e43b48c2e694fffef5b3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c63d199856aa44f4581ec4de75bf3f44
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cbffd9b273517da4a9c4a3218771e0b5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 72c7d2bd3d748db4d988e2204fe1083e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cf1c8b78182704372820a586c1c91d97
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2c247f4266b2864eb96e6a9ae6557d31
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 484e8ad8c4dde382ea67036b32935ef1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,12 @@
|
|||
using Mono.Cecil;
|
||||
|
||||
namespace MLAPI.Editor.CodeGen
|
||||
{
|
||||
internal class PostProcessorReflectionImporterProvider : IReflectionImporterProvider
|
||||
{
|
||||
public IReflectionImporter GetReflectionImporter(ModuleDefinition moduleDefinition)
|
||||
{
|
||||
return new PostProcessorReflectionImporter(moduleDefinition);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f9273a5dad109ab0783891e36c983080
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2c9f2f4b03d774432be69d4c2f53bd2d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fe4fa159f4a96442ba22af67ddf20c65
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2c61e8fe9a68a486fbbc3128d233ded2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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.
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cf89ecbf6f9954c8ea6d0848b1e79d87
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b5aa7a49e9e694f148d810d34577546b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 696c1401e8a6b80409a9a02b51cb469c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ff21a65efe3f4150b3cba2842f170d7d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 26e973580f9aacc46863bde0a99665e6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a4a5c9c08a4038e449fd259764bc663f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 74a8f011a324b7642b69098fe57bf635
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 36e4b519d287d0f4e8bfb7d088a9275f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b8ffd6f352aba584ba0835e2f4221caa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9f4f5bf029cebb64f983b7bdc29f62a1
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Add table
Add a link
Reference in a new issue