mirror of
https://github.com/Project-Redacted/Highscores-Server.git
synced 2025-05-23 11:54:52 +00:00
Add example Unity Project
This commit is contained in:
parent
fda7ff28dd
commit
e3acdb9d6b
7122 changed files with 505543 additions and 2 deletions
|
@ -0,0 +1,4 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly:InternalsVisibleTo("Unity.VisualStudio.EditorTests")]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d791d407901442e49862d3aa783ce8af
|
||||
timeCreated: 1602756877
|
|
@ -0,0 +1,76 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Unity Technologies.
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor
|
||||
{
|
||||
internal class AsyncOperation<T>
|
||||
{
|
||||
private readonly Func<T> _producer;
|
||||
private readonly ManualResetEventSlim _resetEvent;
|
||||
|
||||
private T _result;
|
||||
private Exception _exception;
|
||||
|
||||
private AsyncOperation(Func<T> producer)
|
||||
{
|
||||
_producer = producer;
|
||||
_resetEvent = new ManualResetEventSlim(initialState: false);
|
||||
}
|
||||
|
||||
public static AsyncOperation<T> Run(Func<T> producer)
|
||||
{
|
||||
var task = new AsyncOperation<T>(producer);
|
||||
task.Run();
|
||||
return task;
|
||||
}
|
||||
|
||||
private void Run()
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem(_ =>
|
||||
{
|
||||
try
|
||||
{
|
||||
_result = _producer();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_exception = e;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_resetEvent.Set();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void CheckCompletion()
|
||||
{
|
||||
if (!_resetEvent.IsSet)
|
||||
_resetEvent.Wait();
|
||||
}
|
||||
|
||||
|
||||
public T Result
|
||||
{
|
||||
get
|
||||
{
|
||||
CheckCompletion();
|
||||
return _result;
|
||||
}
|
||||
}
|
||||
|
||||
public Exception Exception
|
||||
{
|
||||
get
|
||||
{
|
||||
CheckCompletion();
|
||||
return _exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c6c8b2f6152bd1348ae35f9f95719f75
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 36422aa067e092e45b9820da2ee3e728
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 41b2d972bdac29e4a89ef08b3b52c248
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Binary file not shown.
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cb67edc1800c2ec4ba8dfb1cf0dfc84a
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,88 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Unity.CodeEditor;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor
|
||||
{
|
||||
internal static class Cli
|
||||
{
|
||||
internal static void Log(string message)
|
||||
{
|
||||
// Use writeline here, instead of UnityEngine.Debug.Log to not include the stacktrace in the editor.log
|
||||
Console.WriteLine($"[VisualStudio.Editor.{nameof(Cli)}] {message}");
|
||||
}
|
||||
|
||||
internal static string GetInstallationDetails(IVisualStudioInstallation installation)
|
||||
{
|
||||
return $"{installation.ToCodeEditorInstallation().Name} Path:{installation.Path}, LanguageVersionSupport:{installation.LatestLanguageVersionSupported} AnalyzersSupport:{installation.SupportsAnalyzers}";
|
||||
}
|
||||
|
||||
internal static void GenerateSolutionWith(VisualStudioEditor vse, string installationPath)
|
||||
{
|
||||
if (vse != null && vse.TryGetVisualStudioInstallationForPath(installationPath, searchInstallations: true, out var vsi))
|
||||
{
|
||||
Log($"Using {GetInstallationDetails(vsi)}");
|
||||
vse.SyncAll();
|
||||
}
|
||||
else
|
||||
{
|
||||
Log($"No Visual Studio installation found in ${installationPath}!");
|
||||
}
|
||||
}
|
||||
|
||||
internal static void GenerateSolution()
|
||||
{
|
||||
if (CodeEditor.CurrentEditor is VisualStudioEditor vse)
|
||||
{
|
||||
Log($"Using default editor settings for Visual Studio installation");
|
||||
GenerateSolutionWith(vse, CodeEditor.CurrentEditorInstallation);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log($"Visual Studio is not set as your default editor, looking for installations");
|
||||
try
|
||||
{
|
||||
var installations = Discovery
|
||||
.GetVisualStudioInstallations()
|
||||
.Cast<VisualStudioInstallation>()
|
||||
.OrderByDescending(vsi => !vsi.IsPrerelease)
|
||||
.ThenBy(vsi => vsi.Version)
|
||||
.ToArray();
|
||||
|
||||
foreach(var vsi in installations)
|
||||
{
|
||||
Log($"Detected {GetInstallationDetails(vsi)}");
|
||||
}
|
||||
|
||||
var installation = installations
|
||||
.FirstOrDefault();
|
||||
|
||||
if (installation != null)
|
||||
{
|
||||
var current = CodeEditor.CurrentEditorInstallation;
|
||||
try
|
||||
{
|
||||
CodeEditor.SetExternalScriptEditor(installation.Path);
|
||||
GenerateSolutionWith(CodeEditor.CurrentEditor as VisualStudioEditor, installation.Path);
|
||||
}
|
||||
finally
|
||||
{
|
||||
CodeEditor.SetExternalScriptEditor(current);
|
||||
}
|
||||
} else
|
||||
{
|
||||
Log($"No Visual Studio installation found!");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"Error detecting Visual Studio installations: {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f7b5530092b3a7646bdc7865f1f6ee94
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,180 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Unity Technologies.
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor
|
||||
{
|
||||
internal static class Discovery
|
||||
{
|
||||
internal const string ManagedWorkload = "Microsoft.VisualStudio.Workload.ManagedGame";
|
||||
|
||||
internal static string _vsWherePath;
|
||||
|
||||
public static void FindVSWhere()
|
||||
{
|
||||
_vsWherePath = FileUtility.GetPackageAssetFullPath("Editor", "VSWhere", "vswhere.exe");
|
||||
}
|
||||
|
||||
public static IEnumerable<IVisualStudioInstallation> GetVisualStudioInstallations()
|
||||
{
|
||||
if (VisualStudioEditor.IsWindows)
|
||||
{
|
||||
foreach (var installation in QueryVsWhere())
|
||||
yield return installation;
|
||||
}
|
||||
|
||||
if (VisualStudioEditor.IsOSX)
|
||||
{
|
||||
var candidates = Directory.EnumerateDirectories("/Applications", "*.app");
|
||||
foreach (var candidate in candidates)
|
||||
{
|
||||
if (TryDiscoverInstallation(candidate, out var installation))
|
||||
yield return installation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsCandidateForDiscovery(string path)
|
||||
{
|
||||
if (File.Exists(path) && VisualStudioEditor.IsWindows && Regex.IsMatch(path, "devenv.exe$", RegexOptions.IgnoreCase))
|
||||
return true;
|
||||
|
||||
if (Directory.Exists(path) && VisualStudioEditor.IsOSX && Regex.IsMatch(path, "Visual\\s?Studio(?!.*Code.*).*.app$", RegexOptions.IgnoreCase))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryDiscoverInstallation(string editorPath, out IVisualStudioInstallation installation)
|
||||
{
|
||||
installation = null;
|
||||
|
||||
if (string.IsNullOrEmpty(editorPath))
|
||||
return false;
|
||||
|
||||
if (!IsCandidateForDiscovery(editorPath))
|
||||
return false;
|
||||
|
||||
// On windows we use the executable directly, so we can query extra information
|
||||
var fvi = editorPath;
|
||||
|
||||
// On Mac we use the .app folder, so we need to access to main assembly
|
||||
if (VisualStudioEditor.IsOSX)
|
||||
{
|
||||
fvi = Path.Combine(editorPath, "Contents/Resources/lib/monodevelop/bin/VisualStudio.exe");
|
||||
|
||||
if (!File.Exists(fvi))
|
||||
fvi = Path.Combine(editorPath, "Contents/MonoBundle/VisualStudio.exe");
|
||||
|
||||
if (!File.Exists(fvi))
|
||||
fvi = Path.Combine(editorPath, "Contents/MonoBundle/VisualStudio.dll");
|
||||
}
|
||||
|
||||
if (!File.Exists(fvi))
|
||||
return false;
|
||||
|
||||
// VS preview are not using the isPrerelease flag so far
|
||||
// On Windows FileDescription contains "Preview", but not on Mac
|
||||
var vi = FileVersionInfo.GetVersionInfo(fvi);
|
||||
var version = new Version(vi.ProductVersion);
|
||||
var isPrerelease = vi.IsPreRelease || string.Concat(editorPath, "/" + vi.FileDescription).ToLower().Contains("preview");
|
||||
|
||||
installation = new VisualStudioInstallation()
|
||||
{
|
||||
IsPrerelease = isPrerelease,
|
||||
Name = $"{vi.FileDescription}{(isPrerelease && VisualStudioEditor.IsOSX ? " Preview" : string.Empty)} [{version.ToString(3)}]",
|
||||
Path = editorPath,
|
||||
Version = version
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
#region VsWhere Json Schema
|
||||
#pragma warning disable CS0649
|
||||
[Serializable]
|
||||
internal class VsWhereResult
|
||||
{
|
||||
public VsWhereEntry[] entries;
|
||||
|
||||
public static VsWhereResult FromJson(string json)
|
||||
{
|
||||
return JsonUtility.FromJson<VsWhereResult>("{ \"" + nameof(VsWhereResult.entries) + "\": " + json + " }");
|
||||
}
|
||||
|
||||
public IEnumerable<VisualStudioInstallation> ToVisualStudioInstallations()
|
||||
{
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
yield return new VisualStudioInstallation()
|
||||
{
|
||||
Name = $"{entry.displayName} [{entry.catalog.productDisplayVersion}]",
|
||||
Path = entry.productPath,
|
||||
IsPrerelease = entry.isPrerelease,
|
||||
Version = Version.Parse(entry.catalog.buildVersion)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
internal class VsWhereEntry
|
||||
{
|
||||
public string displayName;
|
||||
public bool isPrerelease;
|
||||
public string productPath;
|
||||
public VsWhereCatalog catalog;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
internal class VsWhereCatalog
|
||||
{
|
||||
public string productDisplayVersion; // non parseable like "16.3.0 Preview 3.0"
|
||||
public string buildVersion;
|
||||
}
|
||||
#pragma warning restore CS3021
|
||||
#endregion
|
||||
|
||||
private static IEnumerable<VisualStudioInstallation> QueryVsWhere()
|
||||
{
|
||||
var progpath = _vsWherePath;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(progpath))
|
||||
return Enumerable.Empty<VisualStudioInstallation>();
|
||||
|
||||
var process = new Process
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = progpath,
|
||||
Arguments = "-prerelease -format json -utf8",
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
}
|
||||
};
|
||||
|
||||
using (process)
|
||||
{
|
||||
var json = string.Empty;
|
||||
|
||||
process.OutputDataReceived += (o, e) => json += e.Data;
|
||||
process.Start();
|
||||
process.BeginOutputReadLine();
|
||||
process.WaitForExit();
|
||||
|
||||
var result = VsWhereResult.FromJson(json);
|
||||
return result.ToVisualStudioInstallations();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: abe003ac6fee32e4892100a78f555011
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,82 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Unity Technologies.
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor
|
||||
{
|
||||
internal static class FileUtility
|
||||
{
|
||||
public const char WinSeparator = '\\';
|
||||
public const char UnixSeparator = '/';
|
||||
|
||||
public static string GetPackageAssetFullPath(params string[] components)
|
||||
{
|
||||
// Unity has special IO handling of Packages and will resolve those path to the right package location
|
||||
return Path.GetFullPath(Path.Combine("Packages", "com.unity.ide.visualstudio", Path.Combine(components)));
|
||||
}
|
||||
|
||||
public static string GetAssetFullPath(string asset)
|
||||
{
|
||||
var basePath = Path.GetFullPath(Path.Combine(Application.dataPath, ".."));
|
||||
return Path.GetFullPath(Path.Combine(basePath, NormalizePathSeparators(asset)));
|
||||
}
|
||||
|
||||
public static string NormalizePathSeparators(this string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return path;
|
||||
|
||||
if (Path.DirectorySeparatorChar == WinSeparator)
|
||||
path = path.Replace(UnixSeparator, WinSeparator);
|
||||
if (Path.DirectorySeparatorChar == UnixSeparator)
|
||||
path = path.Replace(WinSeparator, UnixSeparator);
|
||||
|
||||
return path.Replace(string.Concat(WinSeparator, WinSeparator), WinSeparator.ToString());
|
||||
}
|
||||
|
||||
public static string NormalizeWindowsToUnix(this string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return path;
|
||||
|
||||
return path.Replace(WinSeparator, UnixSeparator);
|
||||
}
|
||||
|
||||
internal static bool IsFileInProjectRootDirectory(string fileName)
|
||||
{
|
||||
var relative = MakeRelativeToProjectPath(fileName);
|
||||
if (string.IsNullOrEmpty(relative))
|
||||
return false;
|
||||
|
||||
return relative == Path.GetFileName(relative);
|
||||
}
|
||||
|
||||
public static string MakeAbsolutePath(this string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path)) { return string.Empty; }
|
||||
return Path.IsPathRooted(path) ? path : Path.GetFullPath(path);
|
||||
}
|
||||
|
||||
// returns null if outside of the project scope
|
||||
internal static string MakeRelativeToProjectPath(string fileName)
|
||||
{
|
||||
var basePath = Path.GetFullPath(Path.Combine(Application.dataPath, ".."));
|
||||
fileName = NormalizePathSeparators(fileName);
|
||||
|
||||
if (!Path.IsPathRooted(fileName))
|
||||
fileName = Path.Combine(basePath, fileName);
|
||||
|
||||
if (!fileName.StartsWith(basePath, StringComparison.OrdinalIgnoreCase))
|
||||
return null;
|
||||
|
||||
return fileName
|
||||
.Substring(basePath.Length)
|
||||
.Trim(Path.DirectorySeparatorChar);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6f1dc05fb6e7d3e4f89ae9ca482735be
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,102 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor
|
||||
{
|
||||
public sealed class Image : IDisposable
|
||||
{
|
||||
|
||||
long position;
|
||||
Stream stream;
|
||||
|
||||
Image(Stream stream)
|
||||
{
|
||||
this.stream = stream;
|
||||
this.position = stream.Position;
|
||||
this.stream.Position = 0;
|
||||
}
|
||||
|
||||
bool Advance(int length)
|
||||
{
|
||||
if (stream.Position + length >= stream.Length)
|
||||
return false;
|
||||
|
||||
stream.Seek(length, SeekOrigin.Current);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MoveTo(uint position)
|
||||
{
|
||||
if (position >= stream.Length)
|
||||
return false;
|
||||
|
||||
stream.Position = position;
|
||||
return true;
|
||||
}
|
||||
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
stream.Position = position;
|
||||
}
|
||||
|
||||
ushort ReadUInt16()
|
||||
{
|
||||
return (ushort)(stream.ReadByte()
|
||||
| (stream.ReadByte() << 8));
|
||||
}
|
||||
|
||||
uint ReadUInt32()
|
||||
{
|
||||
return (uint)(stream.ReadByte()
|
||||
| (stream.ReadByte() << 8)
|
||||
| (stream.ReadByte() << 16)
|
||||
| (stream.ReadByte() << 24));
|
||||
}
|
||||
|
||||
bool IsManagedAssembly()
|
||||
{
|
||||
if (stream.Length < 318)
|
||||
return false;
|
||||
if (ReadUInt16() != 0x5a4d)
|
||||
return false;
|
||||
if (!Advance(58))
|
||||
return false;
|
||||
if (!MoveTo(ReadUInt32()))
|
||||
return false;
|
||||
if (ReadUInt32() != 0x00004550)
|
||||
return false;
|
||||
if (!Advance(20))
|
||||
return false;
|
||||
if (!Advance(ReadUInt16() == 0x20b ? 222 : 206))
|
||||
return false;
|
||||
|
||||
return ReadUInt32() != 0;
|
||||
}
|
||||
|
||||
public static bool IsAssembly(string file)
|
||||
{
|
||||
if (file == null)
|
||||
throw new ArgumentNullException("file");
|
||||
|
||||
using (var stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
return IsAssembly(stream);
|
||||
}
|
||||
|
||||
public static bool IsAssembly(Stream stream)
|
||||
{
|
||||
if (stream == null)
|
||||
throw new ArgumentNullException(nameof(stream));
|
||||
if (!stream.CanRead)
|
||||
throw new ArgumentException(nameof(stream));
|
||||
if (!stream.CanSeek)
|
||||
throw new ArgumentException(nameof(stream));
|
||||
|
||||
using (var image = new Image(stream))
|
||||
return image.IsManagedAssembly();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8e6c7ea7c059fb547b6723aaf225900b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,14 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor
|
||||
{
|
||||
internal static class KnownAssemblies
|
||||
{
|
||||
public const string Bridge = "SyntaxTree.VisualStudio.Unity.Bridge";
|
||||
public const string Messaging = "SyntaxTree.VisualStudio.Unity.Messaging";
|
||||
public const string UnityVS = "UnityVS.VersionSpecific";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cbccb6292dce08a489e6e742243154e7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2f820130c86c28547a0f1a2f4c73155b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,37 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor.Messaging
|
||||
{
|
||||
internal class Deserializer
|
||||
{
|
||||
private readonly BinaryReader _reader;
|
||||
|
||||
public Deserializer(byte[] buffer)
|
||||
{
|
||||
_reader = new BinaryReader(new MemoryStream(buffer));
|
||||
}
|
||||
|
||||
public int ReadInt32()
|
||||
{
|
||||
return _reader.ReadInt32();
|
||||
}
|
||||
|
||||
public string ReadString()
|
||||
{
|
||||
var length = ReadInt32();
|
||||
return length > 0
|
||||
? Encoding.UTF8.GetString(_reader.ReadBytes(length))
|
||||
: "";
|
||||
}
|
||||
|
||||
public bool CanReadMore()
|
||||
{
|
||||
return _reader.BaseStream.Position < _reader.BaseStream.Length;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3eda7a83649158546826efb3ffe6c1e3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,18 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor.Messaging
|
||||
{
|
||||
internal class ExceptionEventArgs
|
||||
{
|
||||
public Exception Exception { get; }
|
||||
|
||||
public ExceptionEventArgs(Exception exception)
|
||||
{
|
||||
Exception = exception;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 917a51fff055ce547b4ad1215321f3da
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,23 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor.Messaging
|
||||
{
|
||||
internal class Message
|
||||
{
|
||||
public MessageType Type { get; set; }
|
||||
|
||||
public string Value { get; set; }
|
||||
|
||||
public IPEndPoint Origin { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "<Message type:{0} value:{1}>", Type, Value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: de1c9ea7b82c9904d9e5fba2ee70a998
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,19 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
namespace Microsoft.Unity.VisualStudio.Editor.Messaging
|
||||
{
|
||||
internal class MessageEventArgs
|
||||
{
|
||||
public Message Message
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public MessageEventArgs(Message message)
|
||||
{
|
||||
Message = message;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 275143c81d816ef4286fdc67aabc20c8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,48 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
namespace Microsoft.Unity.VisualStudio.Editor.Messaging
|
||||
{
|
||||
internal enum MessageType
|
||||
{
|
||||
None = 0,
|
||||
|
||||
Ping,
|
||||
Pong,
|
||||
|
||||
Play,
|
||||
Stop,
|
||||
Pause,
|
||||
Unpause,
|
||||
|
||||
Build,
|
||||
Refresh,
|
||||
|
||||
Info,
|
||||
Error,
|
||||
Warning,
|
||||
|
||||
Open,
|
||||
Opened,
|
||||
|
||||
Version,
|
||||
UpdatePackage,
|
||||
|
||||
ProjectPath,
|
||||
|
||||
// This message is a technical one for big messages, not intended to be used directly
|
||||
Tcp,
|
||||
|
||||
RunStarted,
|
||||
RunFinished,
|
||||
TestStarted,
|
||||
TestFinished,
|
||||
TestListRetrieved,
|
||||
|
||||
RetrieveTestList,
|
||||
ExecuteTests,
|
||||
|
||||
ShowUsage
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f3edbdc86577af648a23263aa75161e1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,238 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor.Messaging
|
||||
{
|
||||
internal class Messager : IDisposable
|
||||
{
|
||||
public event EventHandler<MessageEventArgs> ReceiveMessage;
|
||||
public event EventHandler<ExceptionEventArgs> MessagerException;
|
||||
|
||||
private readonly UdpSocket _socket;
|
||||
private readonly object _disposeLock = new object();
|
||||
private bool _disposed;
|
||||
|
||||
#if UNITY_EDITOR_WIN
|
||||
[System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool SetHandleInformation(IntPtr hObject, HandleFlags dwMask, HandleFlags dwFlags);
|
||||
|
||||
[Flags]
|
||||
private enum HandleFlags: uint
|
||||
{
|
||||
None = 0,
|
||||
Inherit = 1,
|
||||
ProtectFromClose = 2
|
||||
}
|
||||
#endif
|
||||
|
||||
protected Messager(int port)
|
||||
{
|
||||
_socket = new UdpSocket();
|
||||
_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ExclusiveAddressUse, false);
|
||||
_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
||||
|
||||
#if UNITY_EDITOR_WIN
|
||||
// Explicitely disable inheritance for our UDP socket handle
|
||||
// We found that Unity is creating a fork when importing new assets that can clone our socket
|
||||
SetHandleInformation(_socket.Handle, HandleFlags.Inherit, HandleFlags.None);
|
||||
#endif
|
||||
|
||||
_socket.Bind(IPAddress.Any, port);
|
||||
|
||||
BeginReceiveMessage();
|
||||
}
|
||||
|
||||
private void BeginReceiveMessage()
|
||||
{
|
||||
var buffer = new byte[UdpSocket.BufferSize];
|
||||
var any = UdpSocket.Any();
|
||||
|
||||
try
|
||||
{
|
||||
lock (_disposeLock)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
_socket.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref any, ReceiveMessageCallback, buffer);
|
||||
}
|
||||
}
|
||||
catch (SocketException se)
|
||||
{
|
||||
MessagerException?.Invoke(this, new ExceptionEventArgs(se));
|
||||
|
||||
BeginReceiveMessage();
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void ReceiveMessageCallback(IAsyncResult result)
|
||||
{
|
||||
try
|
||||
{
|
||||
var endPoint = UdpSocket.Any();
|
||||
|
||||
lock (_disposeLock)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
_socket.EndReceiveFrom(result, ref endPoint);
|
||||
}
|
||||
|
||||
var message = DeserializeMessage(UdpSocket.BufferFor(result));
|
||||
if (message != null)
|
||||
{
|
||||
message.Origin = (IPEndPoint)endPoint;
|
||||
|
||||
if (IsValidTcpMessage(message, out var port, out var bufferSize))
|
||||
{
|
||||
// switch to TCP mode to handle big messages
|
||||
TcpClient.Queue(message.Origin.Address, port, bufferSize, buffer =>
|
||||
{
|
||||
var originalMessage = DeserializeMessage(buffer);
|
||||
originalMessage.Origin = message.Origin;
|
||||
ReceiveMessage?.Invoke(this, new MessageEventArgs(originalMessage));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
ReceiveMessage?.Invoke(this, new MessageEventArgs(message));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
RaiseMessagerException(e);
|
||||
}
|
||||
|
||||
BeginReceiveMessage();
|
||||
}
|
||||
|
||||
private static bool IsValidTcpMessage(Message message, out int port, out int bufferSize)
|
||||
{
|
||||
port = 0;
|
||||
bufferSize = 0;
|
||||
if (message.Value == null)
|
||||
return false;
|
||||
if (message.Type != MessageType.Tcp)
|
||||
return false;
|
||||
var parts = message.Value.Split(':');
|
||||
if (parts.Length != 2)
|
||||
return false;
|
||||
if (!int.TryParse(parts[0], out port))
|
||||
return false;
|
||||
return int.TryParse(parts[1], out bufferSize);
|
||||
}
|
||||
|
||||
private void RaiseMessagerException(Exception e)
|
||||
{
|
||||
MessagerException?.Invoke(this, new ExceptionEventArgs(e));
|
||||
}
|
||||
|
||||
private static Message MessageFor(MessageType type, string value)
|
||||
{
|
||||
return new Message { Type = type, Value = value };
|
||||
}
|
||||
|
||||
public void SendMessage(IPEndPoint target, MessageType type, string value = "")
|
||||
{
|
||||
var message = MessageFor(type, value);
|
||||
var buffer = SerializeMessage(message);
|
||||
|
||||
try
|
||||
{
|
||||
lock (_disposeLock)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
if (buffer.Length >= UdpSocket.BufferSize)
|
||||
{
|
||||
// switch to TCP mode to handle big messages
|
||||
var port = TcpListener.Queue(buffer);
|
||||
if (port > 0)
|
||||
{
|
||||
// success, replace original message with "switch to tcp" marker + port information + buffer length
|
||||
message = MessageFor(MessageType.Tcp, string.Concat(port, ':', buffer.Length));
|
||||
buffer = SerializeMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
_socket.BeginSendTo(buffer, 0, Math.Min(buffer.Length, UdpSocket.BufferSize), SocketFlags.None, target, SendMessageCallback, null);
|
||||
}
|
||||
}
|
||||
catch (SocketException se)
|
||||
{
|
||||
MessagerException?.Invoke(this, new ExceptionEventArgs(se));
|
||||
}
|
||||
}
|
||||
|
||||
private void SendMessageCallback(IAsyncResult result)
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (_disposeLock)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
_socket.EndSendTo(result);
|
||||
}
|
||||
}
|
||||
catch (SocketException se)
|
||||
{
|
||||
MessagerException?.Invoke(this, new ExceptionEventArgs(se));
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] SerializeMessage(Message message)
|
||||
{
|
||||
var serializer = new Serializer();
|
||||
serializer.WriteInt32((int)message.Type);
|
||||
serializer.WriteString(message.Value);
|
||||
|
||||
return serializer.Buffer();
|
||||
}
|
||||
|
||||
private static Message DeserializeMessage(byte[] buffer)
|
||||
{
|
||||
if (buffer.Length < 4)
|
||||
return null;
|
||||
|
||||
var deserializer = new Deserializer(buffer);
|
||||
var type = (MessageType)deserializer.ReadInt32();
|
||||
var value = deserializer.ReadString();
|
||||
|
||||
return new Message { Type = type, Value = value };
|
||||
}
|
||||
|
||||
public static Messager BindTo(int port)
|
||||
{
|
||||
return new Messager(port);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (_disposeLock)
|
||||
{
|
||||
_disposed = true;
|
||||
_socket.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5e249ae353801f043a6e4173410c6152
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,43 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor.Messaging
|
||||
{
|
||||
internal class Serializer
|
||||
{
|
||||
private readonly MemoryStream _stream;
|
||||
private readonly BinaryWriter _writer;
|
||||
|
||||
public Serializer()
|
||||
{
|
||||
_stream = new MemoryStream();
|
||||
_writer = new BinaryWriter(_stream);
|
||||
}
|
||||
|
||||
public void WriteInt32(int i)
|
||||
{
|
||||
_writer.Write(i);
|
||||
}
|
||||
|
||||
public void WriteString(string s)
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes(s ?? "");
|
||||
if (bytes.Length > 0)
|
||||
{
|
||||
_writer.Write(bytes.Length);
|
||||
_writer.Write(bytes);
|
||||
}
|
||||
else
|
||||
_writer.Write(0);
|
||||
}
|
||||
|
||||
public byte[] Buffer()
|
||||
{
|
||||
return _stream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 369c09afe05d2c346af49faef943c773
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,93 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor.Messaging
|
||||
{
|
||||
internal class TcpClient
|
||||
{
|
||||
private const int ConnectOrReadTimeoutMilliseconds = 5000;
|
||||
|
||||
private class State
|
||||
{
|
||||
public System.Net.Sockets.TcpClient TcpClient;
|
||||
public NetworkStream NetworkStream;
|
||||
public byte[] Buffer;
|
||||
public Action<byte[]> OnBufferAvailable;
|
||||
}
|
||||
|
||||
public static void Queue(IPAddress address, int port, int bufferSize, Action<byte[]> onBufferAvailable)
|
||||
{
|
||||
var client = new System.Net.Sockets.TcpClient();
|
||||
var state = new State {OnBufferAvailable = onBufferAvailable, TcpClient = client, Buffer = new byte[bufferSize]};
|
||||
|
||||
try
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem(_ =>
|
||||
{
|
||||
var handle = client.BeginConnect(address, port, OnClientConnected, state);
|
||||
if (!handle.AsyncWaitHandle.WaitOne(ConnectOrReadTimeoutMilliseconds))
|
||||
Cleanup(state);
|
||||
});
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Cleanup(state);
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnClientConnected(IAsyncResult result)
|
||||
{
|
||||
var state = (State)result.AsyncState;
|
||||
|
||||
try
|
||||
{
|
||||
state.TcpClient.EndConnect(result);
|
||||
state.NetworkStream = state.TcpClient.GetStream();
|
||||
var handle = state.NetworkStream.BeginRead(state.Buffer, 0, state.Buffer.Length, OnEndRead, state);
|
||||
if (!handle.AsyncWaitHandle.WaitOne(ConnectOrReadTimeoutMilliseconds))
|
||||
Cleanup(state);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Cleanup(state);
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnEndRead(IAsyncResult result)
|
||||
{
|
||||
var state = (State)result.AsyncState;
|
||||
|
||||
try
|
||||
{
|
||||
var count = state.NetworkStream.EndRead(result);
|
||||
if (count == state.Buffer.Length)
|
||||
state.OnBufferAvailable?.Invoke(state.Buffer);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Ignore and cleanup
|
||||
}
|
||||
finally
|
||||
{
|
||||
Cleanup(state);
|
||||
}
|
||||
}
|
||||
|
||||
private static void Cleanup(State state)
|
||||
{
|
||||
state.NetworkStream?.Dispose();
|
||||
state.TcpClient?.Close();
|
||||
|
||||
state.NetworkStream = null;
|
||||
state.TcpClient = null;
|
||||
state.Buffer = null;
|
||||
state.OnBufferAvailable = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f6674c38820d12a49ac116d416521d85
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,82 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor.Messaging
|
||||
{
|
||||
internal class TcpListener
|
||||
{
|
||||
private const int ListenTimeoutMilliseconds = 5000;
|
||||
|
||||
private class State
|
||||
{
|
||||
public System.Net.Sockets.TcpListener TcpListener;
|
||||
public byte[] Buffer;
|
||||
}
|
||||
|
||||
public static int Queue(byte[] buffer)
|
||||
{
|
||||
var tcpListener = new System.Net.Sockets.TcpListener(IPAddress.Any, 0);
|
||||
var state = new State {Buffer = buffer, TcpListener = tcpListener};
|
||||
|
||||
try
|
||||
{
|
||||
tcpListener.Start();
|
||||
|
||||
int port = ((IPEndPoint)tcpListener.LocalEndpoint).Port;
|
||||
|
||||
ThreadPool.QueueUserWorkItem(_ =>
|
||||
{
|
||||
bool listening = true;
|
||||
|
||||
while (listening)
|
||||
{
|
||||
var handle = tcpListener.BeginAcceptTcpClient(OnIncomingConnection, state);
|
||||
listening = handle.AsyncWaitHandle.WaitOne(ListenTimeoutMilliseconds);
|
||||
}
|
||||
|
||||
Cleanup(state);
|
||||
});
|
||||
|
||||
return port;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Cleanup(state);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnIncomingConnection(IAsyncResult result)
|
||||
{
|
||||
var state = (State)result.AsyncState;
|
||||
|
||||
try
|
||||
{
|
||||
using (var client = state.TcpListener.EndAcceptTcpClient(result))
|
||||
{
|
||||
using (var stream = client.GetStream())
|
||||
{
|
||||
stream.Write(state.Buffer, 0, state.Buffer.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Ignore and cleanup
|
||||
}
|
||||
}
|
||||
|
||||
private static void Cleanup(State state)
|
||||
{
|
||||
state.TcpListener?.Stop();
|
||||
|
||||
state.TcpListener = null;
|
||||
state.Buffer = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ded625cf0d03fa94c9f939fd13ced18d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,55 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor.Messaging
|
||||
{
|
||||
internal class UdpSocket : Socket
|
||||
{
|
||||
// Maximum UDP payload is 65507 bytes.
|
||||
// TCP mode will be used when the payload is bigger than this BufferSize
|
||||
public const int BufferSize = 1024 * 8;
|
||||
|
||||
internal UdpSocket()
|
||||
: base(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
|
||||
{
|
||||
SetIOControl();
|
||||
}
|
||||
|
||||
public void Bind(IPAddress address, int port = 0)
|
||||
{
|
||||
Bind(new IPEndPoint(address ?? IPAddress.Any, port));
|
||||
}
|
||||
|
||||
private void SetIOControl()
|
||||
{
|
||||
if (!VisualStudioEditor.IsWindows)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
const int SIO_UDP_CONNRESET = -1744830452;
|
||||
|
||||
IOControl(SIO_UDP_CONNRESET, new byte[] { 0 }, new byte[0]);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// fallback
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] BufferFor(IAsyncResult result)
|
||||
{
|
||||
return (byte[])result.AsyncState;
|
||||
}
|
||||
|
||||
public static EndPoint Any()
|
||||
{
|
||||
return new IPEndPoint(IPAddress.Any, 0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 38cb3a4a17d2cfd41926da95ce675934
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1e5abb64fdd0542b38f4dc1b60343e8a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1 @@
|
|||
AppleEventIntegration.bundle/Contents/MacOS/AppleEventIntegration binary
|
|
@ -0,0 +1,28 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a20df6e3467b24ed5b49c857ce39e096
|
||||
folderAsset: yes
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 543eb5eeeb1d5424ca8876b93fad5326
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,48 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildMachineOSBuild</key>
|
||||
<string>19G2021</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>AppleEventIntegration</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.unity.visualstudio.AppleEventIntegration</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>AppleEventIntegration</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>MacOSX</string>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>DTCompiler</key>
|
||||
<string>com.apple.compilers.llvm.clang.1_0</string>
|
||||
<key>DTPlatformBuild</key>
|
||||
<string>12B45b</string>
|
||||
<key>DTPlatformName</key>
|
||||
<string>macosx</string>
|
||||
<key>DTPlatformVersion</key>
|
||||
<string>11.0</string>
|
||||
<key>DTSDKBuild</key>
|
||||
<string>20A2408</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>macosx11.0</string>
|
||||
<key>DTXcode</key>
|
||||
<string>1220</string>
|
||||
<key>DTXcodeBuild</key>
|
||||
<string>12B45b</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.13</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2019 Unity. All rights reserved.</string>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 29239d79a3471495e9d270601006dad7
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e811c7e1c1e9a4b50b237772d317959f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Binary file not shown.
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9c3599bc139404df2955d3ffd39d60d6
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 11ca2399a9422473eb66bca747f3ad52
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,115 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>files</key>
|
||||
<dict/>
|
||||
<key>files2</key>
|
||||
<dict/>
|
||||
<key>rules</key>
|
||||
<dict>
|
||||
<key>^Resources/</key>
|
||||
<true/>
|
||||
<key>^Resources/.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^Resources/.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Resources/Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^version.plist$</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>rules2</key>
|
||||
<dict>
|
||||
<key>.*\.dSYM($|/)</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>11</real>
|
||||
</dict>
|
||||
<key>^(.*/)?\.DS_Store$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>2000</real>
|
||||
</dict>
|
||||
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
|
||||
<dict>
|
||||
<key>nested</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>10</real>
|
||||
</dict>
|
||||
<key>^.*</key>
|
||||
<true/>
|
||||
<key>^Info\.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^PkgInfo$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^Resources/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^Resources/.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^Resources/.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Resources/Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^[^/]+$</key>
|
||||
<dict>
|
||||
<key>nested</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>10</real>
|
||||
</dict>
|
||||
<key>^embedded\.provisionprofile$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^version\.plist$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3379e8bd711774041a330f218af69b7a
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8beeeeebc0857854d8b4e2c2895dd7a9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,207 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Unity Technologies.
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Compilation;
|
||||
using UnityEditor.PackageManager;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor
|
||||
{
|
||||
public interface IAssemblyNameProvider
|
||||
{
|
||||
string[] ProjectSupportedExtensions { get; }
|
||||
string ProjectGenerationRootNamespace { get; }
|
||||
ProjectGenerationFlag ProjectGenerationFlag { get; }
|
||||
|
||||
string GetAssemblyNameFromScriptPath(string path);
|
||||
string GetAssemblyName(string assemblyOutputPath, string assemblyName);
|
||||
bool IsInternalizedPackagePath(string path);
|
||||
IEnumerable<Assembly> GetAssemblies(Func<string, bool> shouldFileBePartOfSolution);
|
||||
IEnumerable<string> GetAllAssetPaths();
|
||||
UnityEditor.PackageManager.PackageInfo FindForAssetPath(string assetPath);
|
||||
ResponseFileData ParseResponseFile(string responseFilePath, string projectDirectory, string[] systemReferenceDirectories);
|
||||
void ToggleProjectGeneration(ProjectGenerationFlag preference);
|
||||
}
|
||||
|
||||
public class AssemblyNameProvider : IAssemblyNameProvider
|
||||
{
|
||||
private readonly Dictionary<string, UnityEditor.PackageManager.PackageInfo> m_PackageInfoCache = new Dictionary<string, UnityEditor.PackageManager.PackageInfo>();
|
||||
|
||||
ProjectGenerationFlag m_ProjectGenerationFlag = (ProjectGenerationFlag)EditorPrefs.GetInt(
|
||||
"unity_project_generation_flag",
|
||||
(int)(ProjectGenerationFlag.Local | ProjectGenerationFlag.Embedded));
|
||||
|
||||
public string[] ProjectSupportedExtensions => EditorSettings.projectGenerationUserExtensions;
|
||||
|
||||
public string ProjectGenerationRootNamespace => EditorSettings.projectGenerationRootNamespace;
|
||||
|
||||
public ProjectGenerationFlag ProjectGenerationFlag
|
||||
{
|
||||
get => m_ProjectGenerationFlag;
|
||||
private set
|
||||
{
|
||||
EditorPrefs.SetInt("unity_project_generation_flag", (int)value);
|
||||
m_ProjectGenerationFlag = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string GetAssemblyNameFromScriptPath(string path)
|
||||
{
|
||||
return CompilationPipeline.GetAssemblyNameFromScriptPath(path);
|
||||
}
|
||||
|
||||
public IEnumerable<Assembly> GetAssemblies(Func<string, bool> shouldFileBePartOfSolution)
|
||||
{
|
||||
IEnumerable<Assembly> assemblies = GetAssembliesByType(AssembliesType.Editor, shouldFileBePartOfSolution, @"Temp\Bin\Debug\");
|
||||
|
||||
if (!ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.PlayerAssemblies))
|
||||
{
|
||||
return assemblies;
|
||||
}
|
||||
var playerAssemblies = GetAssembliesByType(AssembliesType.Player, shouldFileBePartOfSolution, @"Temp\Bin\Debug\Player\");
|
||||
return assemblies.Concat(playerAssemblies);
|
||||
}
|
||||
|
||||
private static IEnumerable<Assembly> GetAssembliesByType(AssembliesType type, Func<string, bool> shouldFileBePartOfSolution, string outputPath)
|
||||
{
|
||||
foreach (var assembly in CompilationPipeline.GetAssemblies(type))
|
||||
{
|
||||
if (assembly.sourceFiles.Any(shouldFileBePartOfSolution))
|
||||
{
|
||||
yield return new Assembly(
|
||||
assembly.name,
|
||||
outputPath,
|
||||
assembly.sourceFiles,
|
||||
assembly.defines,
|
||||
assembly.assemblyReferences,
|
||||
assembly.compiledAssemblyReferences,
|
||||
assembly.flags,
|
||||
assembly.compilerOptions
|
||||
#if UNITY_2020_2_OR_NEWER
|
||||
, assembly.rootNamespace
|
||||
#endif
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string GetCompileOutputPath(string assemblyName)
|
||||
{
|
||||
return assemblyName.EndsWith(".Player", StringComparison.Ordinal) ? @"Temp\Bin\Debug\Player\" : @"Temp\Bin\Debug\";
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetAllAssetPaths()
|
||||
{
|
||||
return AssetDatabase.GetAllAssetPaths();
|
||||
}
|
||||
|
||||
private static string ResolvePotentialParentPackageAssetPath(string assetPath)
|
||||
{
|
||||
const string packagesPrefix = "packages/";
|
||||
if (!assetPath.StartsWith(packagesPrefix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var followupSeparator = assetPath.IndexOf('/', packagesPrefix.Length);
|
||||
if (followupSeparator == -1)
|
||||
{
|
||||
return assetPath.ToLowerInvariant();
|
||||
}
|
||||
|
||||
return assetPath.Substring(0, followupSeparator).ToLowerInvariant();
|
||||
}
|
||||
|
||||
public UnityEditor.PackageManager.PackageInfo FindForAssetPath(string assetPath)
|
||||
{
|
||||
var parentPackageAssetPath = ResolvePotentialParentPackageAssetPath(assetPath);
|
||||
if (parentPackageAssetPath == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (m_PackageInfoCache.TryGetValue(parentPackageAssetPath, out var cachedPackageInfo))
|
||||
{
|
||||
return cachedPackageInfo;
|
||||
}
|
||||
|
||||
var result = UnityEditor.PackageManager.PackageInfo.FindForAssetPath(parentPackageAssetPath);
|
||||
m_PackageInfoCache[parentPackageAssetPath] = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool IsInternalizedPackagePath(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path.Trim()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var packageInfo = FindForAssetPath(path);
|
||||
if (packageInfo == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var packageSource = packageInfo.source;
|
||||
switch (packageSource)
|
||||
{
|
||||
case PackageSource.Embedded:
|
||||
return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.Embedded);
|
||||
case PackageSource.Registry:
|
||||
return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.Registry);
|
||||
case PackageSource.BuiltIn:
|
||||
return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.BuiltIn);
|
||||
case PackageSource.Unknown:
|
||||
return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.Unknown);
|
||||
case PackageSource.Local:
|
||||
return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.Local);
|
||||
case PackageSource.Git:
|
||||
return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.Git);
|
||||
case PackageSource.LocalTarball:
|
||||
return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.LocalTarBall);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public ResponseFileData ParseResponseFile(string responseFilePath, string projectDirectory, string[] systemReferenceDirectories)
|
||||
{
|
||||
return CompilationPipeline.ParseResponseFile(
|
||||
responseFilePath,
|
||||
projectDirectory,
|
||||
systemReferenceDirectories
|
||||
);
|
||||
}
|
||||
|
||||
public void ToggleProjectGeneration(ProjectGenerationFlag preference)
|
||||
{
|
||||
if (ProjectGenerationFlag.HasFlag(preference))
|
||||
{
|
||||
ProjectGenerationFlag ^= preference;
|
||||
}
|
||||
else
|
||||
{
|
||||
ProjectGenerationFlag |= preference;
|
||||
}
|
||||
}
|
||||
|
||||
internal void ResetPackageInfoCache()
|
||||
{
|
||||
m_PackageInfoCache.Clear();
|
||||
}
|
||||
|
||||
public void ResetProjectGenerationFlag()
|
||||
{
|
||||
ProjectGenerationFlag = ProjectGenerationFlag.None;
|
||||
}
|
||||
|
||||
public string GetAssemblyName(string assemblyOutputPath, string assemblyName)
|
||||
{
|
||||
return assemblyOutputPath.EndsWith(@"\Player\", StringComparison.Ordinal) ? assemblyName + ".Player" : assemblyName;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 57537f08f8e923f488e4aadabb831c9b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,36 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Unity Technologies.
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor
|
||||
{
|
||||
public interface IFileIO
|
||||
{
|
||||
bool Exists(string fileName);
|
||||
|
||||
string ReadAllText(string fileName);
|
||||
void WriteAllText(string fileName, string content);
|
||||
}
|
||||
|
||||
class FileIOProvider : IFileIO
|
||||
{
|
||||
public bool Exists(string fileName)
|
||||
{
|
||||
return File.Exists(fileName);
|
||||
}
|
||||
|
||||
public string ReadAllText(string fileName)
|
||||
{
|
||||
return File.ReadAllText(fileName);
|
||||
}
|
||||
|
||||
public void WriteAllText(string fileName, string content)
|
||||
{
|
||||
File.WriteAllText(fileName, content, Encoding.UTF8);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ec80b1fb8938b3b4ab442d10390c5315
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,26 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Unity Technologies.
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
namespace Microsoft.Unity.VisualStudio.Editor
|
||||
{
|
||||
public interface IGUIDGenerator
|
||||
{
|
||||
string ProjectGuid(string projectName, string assemblyName);
|
||||
string SolutionGuid(string projectName, ScriptingLanguage scriptingLanguage);
|
||||
}
|
||||
|
||||
class GUIDProvider : IGUIDGenerator
|
||||
{
|
||||
public string ProjectGuid(string projectName, string assemblyName)
|
||||
{
|
||||
return SolutionGuidGenerator.GuidForProject(projectName + assemblyName);
|
||||
}
|
||||
|
||||
public string SolutionGuid(string projectName, ScriptingLanguage scriptingLanguage)
|
||||
{
|
||||
return SolutionGuidGenerator.GuidForSolution(projectName, scriptingLanguage);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7652904b1008e324fb7cfb952ea87656
|
||||
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: 9f3705b95d031e84c82f140d8e980867
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,23 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Unity Technologies.
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor
|
||||
{
|
||||
[Flags]
|
||||
public enum ProjectGenerationFlag
|
||||
{
|
||||
None = 0,
|
||||
Embedded = 1,
|
||||
Local = 2,
|
||||
Registry = 4,
|
||||
Git = 8,
|
||||
BuiltIn = 16,
|
||||
Unknown = 32,
|
||||
PlayerAssemblies = 64,
|
||||
LocalTarBall = 128,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 555fcccd6b79a864f83e7a319daa1c3e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor
|
||||
{
|
||||
internal class ProjectProperties
|
||||
{
|
||||
public string ProjectGuid { get; set; } = string.Empty;
|
||||
public string LangVersion { get; set; } = "latest";
|
||||
public string AssemblyName { get; set; } = string.Empty;
|
||||
public string RootNamespace { get; set; } = string.Empty;
|
||||
public string OutputPath { get; set; } = string.Empty;
|
||||
|
||||
// Analyzers
|
||||
public string[] Analyzers { get; set; } = Array.Empty<string>();
|
||||
public string RulesetPath { get; set; } = string.Empty;
|
||||
|
||||
// RSP alterable
|
||||
public string[] Defines { get; set; } = Array.Empty<string>();
|
||||
public bool Unsafe { get; set; } = false;
|
||||
|
||||
// VSTU Flavouring
|
||||
public string FlavoringProjectType { get; set; } = string.Empty;
|
||||
public string FlavoringBuildTarget { get; set; } = string.Empty;
|
||||
public string FlavoringUnityVersion { get; set; } = string.Empty;
|
||||
public string FlavoringPackageVersion { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fa7011e2ea1ff024083fea2179f3df08
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,12 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
namespace Microsoft.Unity.VisualStudio.Editor
|
||||
{
|
||||
internal class Solution
|
||||
{
|
||||
public SolutionProjectEntry[] Projects { get; set; }
|
||||
public SolutionProperties[] Properties { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: af4c2c762e1d8e949a6bc458973df6e7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,80 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor
|
||||
{
|
||||
internal static class SolutionParser
|
||||
{
|
||||
// Compared to the bridge implementation, we are not returning "{" "}" from Guids
|
||||
private static readonly Regex ProjectDeclaration = new Regex(@"Project\(\""{(?<projectFactoryGuid>.*?)}\""\)\s+=\s+\""(?<name>.*?)\"",\s+\""(?<fileName>.*?)\"",\s+\""{(?<projectGuid>.*?)}\""(?<metadata>.*?)\bEndProject\b", RegexOptions.Singleline | RegexOptions.ExplicitCapture);
|
||||
private static readonly Regex PropertiesDeclaration = new Regex(@"GlobalSection\((?<name>([\w]+Properties|NestedProjects))\)\s+=\s+(?<type>(?:post|pre)Solution)(?<entries>.*?)EndGlobalSection", RegexOptions.Singleline | RegexOptions.ExplicitCapture);
|
||||
private static readonly Regex PropertiesEntryDeclaration = new Regex(@"^\s*(?<key>.*?)=(?<value>.*?)$", RegexOptions.Multiline | RegexOptions.ExplicitCapture);
|
||||
|
||||
public static Solution ParseSolutionFile(string filename, IFileIO fileIO)
|
||||
{
|
||||
return ParseSolutionContent(fileIO.ReadAllText(filename));
|
||||
}
|
||||
|
||||
public static Solution ParseSolutionContent(string content)
|
||||
{
|
||||
return new Solution
|
||||
{
|
||||
Projects = ParseSolutionProjects(content),
|
||||
Properties = ParseSolutionProperties(content)
|
||||
};
|
||||
}
|
||||
|
||||
private static SolutionProjectEntry[] ParseSolutionProjects(string content)
|
||||
{
|
||||
var projects = new List<SolutionProjectEntry>();
|
||||
var mc = ProjectDeclaration.Matches(content);
|
||||
|
||||
foreach (Match match in mc)
|
||||
{
|
||||
projects.Add(new SolutionProjectEntry
|
||||
{
|
||||
ProjectFactoryGuid = match.Groups["projectFactoryGuid"].Value,
|
||||
Name = match.Groups["name"].Value,
|
||||
FileName = match.Groups["fileName"].Value,
|
||||
ProjectGuid = match.Groups["projectGuid"].Value,
|
||||
Metadata = match.Groups["metadata"].Value
|
||||
});
|
||||
}
|
||||
|
||||
return projects.ToArray();
|
||||
}
|
||||
|
||||
private static SolutionProperties[] ParseSolutionProperties(string content)
|
||||
{
|
||||
var properties = new List<SolutionProperties>();
|
||||
var mc = PropertiesDeclaration.Matches(content);
|
||||
|
||||
foreach (Match match in mc)
|
||||
{
|
||||
var sp = new SolutionProperties
|
||||
{
|
||||
Entries = new List<KeyValuePair<string, string>>(),
|
||||
Name = match.Groups["name"].Value,
|
||||
Type = match.Groups["type"].Value
|
||||
};
|
||||
|
||||
var entries = match.Groups["entries"].Value;
|
||||
var mec = PropertiesEntryDeclaration.Matches(entries);
|
||||
foreach (Match entry in mec)
|
||||
{
|
||||
var key = entry.Groups["key"].Value.Trim();
|
||||
var value = entry.Groups["value"].Value.Trim();
|
||||
sp.Entries.Add(new KeyValuePair<string, string>(key, value));
|
||||
}
|
||||
|
||||
properties.Add(sp);
|
||||
}
|
||||
|
||||
return properties.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fbbb1ee655846b043baf6c3502b5ce49
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,22 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor
|
||||
{
|
||||
internal class SolutionProjectEntry
|
||||
{
|
||||
public string ProjectFactoryGuid { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string FileName { get; set; }
|
||||
public string ProjectGuid { get; set; }
|
||||
public string Metadata { get; set; }
|
||||
|
||||
public bool IsSolutionFolderProjectFactory()
|
||||
{
|
||||
return ProjectFactoryGuid != null && ProjectFactoryGuid.Equals("2150E333-8FDC-42A3-9474-1A3956D46DE8", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5c1b8a755d2c97640bbb207c43f4cf61
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,15 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor
|
||||
{
|
||||
internal class SolutionProperties
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public IList<KeyValuePair<string, string>> Entries { get; set; }
|
||||
public string Type { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 829d4d6bc39fd1044ba4c5fc2a9c911f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,30 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor
|
||||
{
|
||||
internal static class Symbols
|
||||
{
|
||||
public static bool IsPortableSymbolFile(string pdbFile)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var stream = File.OpenRead(pdbFile))
|
||||
{
|
||||
return stream.ReadByte() == 'B'
|
||||
&& stream.ReadByte() == 'S'
|
||||
&& stream.ReadByte() == 'J'
|
||||
&& stream.ReadByte() == 'B';
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b9308b762484008498bb5cd1886aa491
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7f9f1d015d7a8ba46b7d71acfcda3ae7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor.Testing
|
||||
{
|
||||
[Serializable]
|
||||
internal class TestAdaptorContainer
|
||||
{
|
||||
public TestAdaptor[] TestAdaptors;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
internal class TestAdaptor
|
||||
{
|
||||
public string Id;
|
||||
public string Name;
|
||||
public string FullName;
|
||||
|
||||
public string Type;
|
||||
public string Method;
|
||||
public string Assembly;
|
||||
|
||||
public int Parent;
|
||||
|
||||
public TestAdaptor(ITestAdaptor testAdaptor, int parent)
|
||||
{
|
||||
Id = testAdaptor.Id;
|
||||
Name = testAdaptor.Name;
|
||||
FullName = testAdaptor.FullName;
|
||||
|
||||
Type = testAdaptor.TypeInfo?.FullName;
|
||||
Method = testAdaptor.Method?.Name;
|
||||
Assembly = testAdaptor.TypeInfo?.Assembly?.Location;
|
||||
|
||||
Parent = parent;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b73b3de0d473d4a1c887ab31f69b1a8d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor.Testing
|
||||
{
|
||||
[Serializable]
|
||||
internal class TestResultAdaptorContainer
|
||||
{
|
||||
public TestResultAdaptor[] TestResultAdaptors;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
internal class TestResultAdaptor
|
||||
{
|
||||
public string Name;
|
||||
public string FullName;
|
||||
|
||||
public int PassCount;
|
||||
public int FailCount;
|
||||
public int InconclusiveCount;
|
||||
public int SkipCount;
|
||||
|
||||
public string ResultState;
|
||||
public string StackTrace;
|
||||
|
||||
public TestStatusAdaptor TestStatus;
|
||||
|
||||
public int Parent;
|
||||
|
||||
public TestResultAdaptor(ITestResultAdaptor testResultAdaptor, int parent)
|
||||
{
|
||||
Name = testResultAdaptor.Name;
|
||||
FullName = testResultAdaptor.FullName;
|
||||
|
||||
PassCount = testResultAdaptor.PassCount;
|
||||
FailCount = testResultAdaptor.FailCount;
|
||||
InconclusiveCount = testResultAdaptor.InconclusiveCount;
|
||||
SkipCount = testResultAdaptor.SkipCount;
|
||||
|
||||
switch (testResultAdaptor.TestStatus)
|
||||
{
|
||||
case UnityEditor.TestTools.TestRunner.Api.TestStatus.Passed:
|
||||
TestStatus = TestStatusAdaptor.Passed;
|
||||
break;
|
||||
case UnityEditor.TestTools.TestRunner.Api.TestStatus.Skipped:
|
||||
TestStatus = TestStatusAdaptor.Skipped;
|
||||
break;
|
||||
case UnityEditor.TestTools.TestRunner.Api.TestStatus.Inconclusive:
|
||||
TestStatus = TestStatusAdaptor.Inconclusive;
|
||||
break;
|
||||
case UnityEditor.TestTools.TestRunner.Api.TestStatus.Failed:
|
||||
TestStatus = TestStatusAdaptor.Failed;
|
||||
break;
|
||||
}
|
||||
|
||||
Parent = parent;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f47f2d030bc1d415a8d15a51dbcc39a2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor.Testing
|
||||
{
|
||||
[InitializeOnLoad]
|
||||
internal class TestRunnerApiListener
|
||||
{
|
||||
private static readonly TestRunnerApi _testRunnerApi;
|
||||
private static readonly TestRunnerCallbacks _testRunnerCallbacks;
|
||||
|
||||
static TestRunnerApiListener()
|
||||
{
|
||||
if (!VisualStudioEditor.IsEnabled)
|
||||
return;
|
||||
|
||||
_testRunnerApi = ScriptableObject.CreateInstance<TestRunnerApi>();
|
||||
_testRunnerCallbacks = new TestRunnerCallbacks();
|
||||
|
||||
_testRunnerApi.RegisterCallbacks(_testRunnerCallbacks);
|
||||
}
|
||||
|
||||
public static void RetrieveTestList(string mode)
|
||||
{
|
||||
RetrieveTestList((TestMode) Enum.Parse(typeof(TestMode), mode));
|
||||
}
|
||||
|
||||
private static void RetrieveTestList(TestMode mode)
|
||||
{
|
||||
_testRunnerApi?.RetrieveTestList(mode, ta => _testRunnerCallbacks.TestListRetrieved(mode, ta));
|
||||
}
|
||||
|
||||
public static void ExecuteTests(string command)
|
||||
{
|
||||
// ExecuteTests format:
|
||||
// TestMode:FullName
|
||||
|
||||
var index = command.IndexOf(':');
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
var testMode = (TestMode)Enum.Parse(typeof(TestMode), command.Substring(0, index));
|
||||
var filter = command.Substring(index + 1);
|
||||
|
||||
ExecuteTests(new Filter { testMode = testMode, testNames = new [] { filter } });
|
||||
}
|
||||
|
||||
private static void ExecuteTests(Filter filter)
|
||||
{
|
||||
_testRunnerApi?.Execute(new ExecutionSettings(filter));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0b59b40c84c6a5348a188c16b17c7b40
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,90 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor.Testing
|
||||
{
|
||||
internal class TestRunnerCallbacks : ICallbacks
|
||||
{
|
||||
private string Serialize<TContainer, TSource, TAdaptor>(
|
||||
TSource source,
|
||||
Func<TSource, int, TAdaptor> createAdaptor,
|
||||
Func<TSource, IEnumerable<TSource>> children,
|
||||
Func<TAdaptor[], TContainer> container)
|
||||
{
|
||||
var adaptors = new List<TAdaptor>();
|
||||
|
||||
void AddAdaptor(TSource item, int parentIndex)
|
||||
{
|
||||
var index = adaptors.Count;
|
||||
adaptors.Add(createAdaptor(item, parentIndex));
|
||||
foreach (var child in children(item))
|
||||
AddAdaptor(child, index);
|
||||
}
|
||||
|
||||
AddAdaptor(source, -1);
|
||||
|
||||
return JsonUtility.ToJson(container(adaptors.ToArray()));
|
||||
}
|
||||
|
||||
private string Serialize(ITestAdaptor testAdaptor)
|
||||
{
|
||||
return Serialize(
|
||||
testAdaptor,
|
||||
(a, parentIndex) => new TestAdaptor(a, parentIndex),
|
||||
(a) => a.Children,
|
||||
(r) => new TestAdaptorContainer { TestAdaptors = r });
|
||||
}
|
||||
|
||||
private string Serialize(ITestResultAdaptor testResultAdaptor)
|
||||
{
|
||||
return Serialize(
|
||||
testResultAdaptor,
|
||||
(a, parentIndex) => new TestResultAdaptor(a, parentIndex),
|
||||
(a) => a.Children,
|
||||
(r) => new TestResultAdaptorContainer { TestResultAdaptors = r });
|
||||
}
|
||||
|
||||
public void RunFinished(ITestResultAdaptor testResultAdaptor)
|
||||
{
|
||||
VisualStudioIntegration.BroadcastMessage(Messaging.MessageType.RunFinished, Serialize(testResultAdaptor));
|
||||
}
|
||||
|
||||
public void RunStarted(ITestAdaptor testAdaptor)
|
||||
{
|
||||
VisualStudioIntegration.BroadcastMessage(Messaging.MessageType.RunStarted, Serialize(testAdaptor));
|
||||
}
|
||||
|
||||
public void TestFinished(ITestResultAdaptor testResultAdaptor)
|
||||
{
|
||||
VisualStudioIntegration.BroadcastMessage(Messaging.MessageType.TestFinished, Serialize(testResultAdaptor));
|
||||
}
|
||||
|
||||
public void TestStarted(ITestAdaptor testAdaptor)
|
||||
{
|
||||
VisualStudioIntegration.BroadcastMessage(Messaging.MessageType.TestStarted, Serialize(testAdaptor));
|
||||
}
|
||||
|
||||
private static string TestModeName(TestMode testMode)
|
||||
{
|
||||
switch (testMode)
|
||||
{
|
||||
case TestMode.EditMode: return "EditMode";
|
||||
case TestMode.PlayMode: return "PlayMode";
|
||||
}
|
||||
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
|
||||
internal void TestListRetrieved(TestMode testMode, ITestAdaptor testAdaptor)
|
||||
{
|
||||
// TestListRetrieved format:
|
||||
// TestMode:Json
|
||||
|
||||
var value = TestModeName(testMode) + ":" + Serialize(testAdaptor);
|
||||
VisualStudioIntegration.BroadcastMessage(Messaging.MessageType.TestListRetrieved, value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fae6007c1ac2cc744b2891fd4d279c96
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor.Testing
|
||||
{
|
||||
[Serializable]
|
||||
internal enum TestStatusAdaptor
|
||||
{
|
||||
Passed,
|
||||
Skipped,
|
||||
Inconclusive,
|
||||
Failed,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0719f1a8b2a284e1182b352e6c8c3c60
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,54 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
using System;
|
||||
using UnityEditor.Compilation;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor
|
||||
{
|
||||
internal static class UnityInstallation
|
||||
{
|
||||
public static bool IsMainUnityEditorProcess
|
||||
{
|
||||
get
|
||||
{
|
||||
#if UNITY_2020_2_OR_NEWER
|
||||
if (UnityEditor.AssetDatabase.IsAssetImportWorkerProcess())
|
||||
return false;
|
||||
#elif UNITY_2019_3_OR_NEWER
|
||||
if (UnityEditor.Experimental.AssetDatabaseExperimental.IsAssetImportWorkerProcess())
|
||||
return false;
|
||||
#endif
|
||||
|
||||
#if UNITY_2021_1_OR_NEWER
|
||||
if (UnityEditor.MPE.ProcessService.level == UnityEditor.MPE.ProcessLevel.Secondary)
|
||||
return false;
|
||||
#elif UNITY_2020_2_OR_NEWER
|
||||
if (UnityEditor.MPE.ProcessService.level == UnityEditor.MPE.ProcessLevel.Slave)
|
||||
return false;
|
||||
#elif UNITY_2020_1_OR_NEWER
|
||||
if (global::Unity.MPE.ProcessService.level == global::Unity.MPE.ProcessLevel.UMP_SLAVE)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static Version LatestLanguageVersionSupported(Assembly assembly)
|
||||
{
|
||||
#if UNITY_2020_2_OR_NEWER
|
||||
if (assembly?.compilerOptions != null && Version.TryParse(assembly.compilerOptions.LanguageVersion, out var result))
|
||||
return result;
|
||||
|
||||
// if parsing fails, we know at least we have support for 8.0
|
||||
return new Version(8, 0);
|
||||
#else
|
||||
return new Version(7, 3);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a8c76505bcc613640ade706bdb0f1cba
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,118 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor
|
||||
{
|
||||
[Serializable]
|
||||
internal class FileUsage
|
||||
{
|
||||
public string Path;
|
||||
public string[] GameObjectPath;
|
||||
}
|
||||
|
||||
internal static class UsageUtility
|
||||
{
|
||||
internal static void ShowUsage(string json)
|
||||
{
|
||||
try
|
||||
{
|
||||
var usage = JsonUtility.FromJson<FileUsage>(json);
|
||||
ShowUsage(usage.Path, usage.GameObjectPath);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignore malformed request
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ShowUsage(string path, string[] gameObjectPath)
|
||||
{
|
||||
path = FileUtility.MakeRelativeToProjectPath(path);
|
||||
if (path == null)
|
||||
return;
|
||||
|
||||
path = FileUtility.NormalizeWindowsToUnix(path);
|
||||
var extension = Path.GetExtension(path).ToLower();
|
||||
|
||||
EditorUtility.FocusProjectWindow();
|
||||
|
||||
switch (extension)
|
||||
{
|
||||
case ".unity":
|
||||
ShowSceneUsage(path, gameObjectPath);
|
||||
break;
|
||||
default:
|
||||
var asset = AssetDatabase.LoadMainAssetAtPath(path);
|
||||
Selection.activeObject = asset;
|
||||
EditorGUIUtility.PingObject(asset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ShowSceneUsage(string scenePath, string[] gameObjectPath)
|
||||
{
|
||||
var scene = SceneManager.GetSceneByPath(scenePath.Replace(Path.DirectorySeparatorChar, '/'));
|
||||
if (!scene.isLoaded)
|
||||
{
|
||||
var result = EditorUtility.DisplayDialogComplex("Show Usage",
|
||||
$"Do you want to open \"{Path.GetFileName(scenePath)}\"?",
|
||||
"Open Scene",
|
||||
"Cancel",
|
||||
"Open Scene (additive)");
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case 0:
|
||||
EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo();
|
||||
scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single);
|
||||
break;
|
||||
case 1:
|
||||
return;
|
||||
case 2:
|
||||
scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Additive);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ShowSceneUsage(scene, gameObjectPath);
|
||||
}
|
||||
|
||||
private static void ShowSceneUsage(Scene scene, string[] gameObjectPath)
|
||||
{
|
||||
if (gameObjectPath == null || gameObjectPath.Length == 0)
|
||||
return;
|
||||
|
||||
var go = scene.GetRootGameObjects().FirstOrDefault(g => g.name == gameObjectPath[0]);
|
||||
if (go == null)
|
||||
return;
|
||||
|
||||
for (var ni = 1; ni < gameObjectPath.Length; ni++)
|
||||
{
|
||||
var transform = go.transform;
|
||||
for (var i = 0; i < transform.childCount; i++)
|
||||
{
|
||||
var child = transform.GetChild(i);
|
||||
var childgo = child.gameObject;
|
||||
if (childgo.name == gameObjectPath[ni])
|
||||
{
|
||||
go = childgo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Selection.activeObject = go;
|
||||
EditorGUIUtility.PingObject(go);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5a7aba2d3d458e04eb4210c0303fbf64
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5b17896803f77494da73d73448fb6cb4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Binary file not shown.
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 585c3fb85b32bd64e8814074e754163e
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor
|
||||
{
|
||||
internal struct VersionPair
|
||||
{
|
||||
public Version IdeVersion;
|
||||
public Version LanguageVersion;
|
||||
|
||||
public VersionPair(int idemajor, int ideminor, int languageMajor, int languageMinor)
|
||||
{
|
||||
IdeVersion = new Version(idemajor, ideminor);
|
||||
LanguageVersion = new Version(languageMajor, languageMinor);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ffe1bdf971d321f4db593c4c6ebd6e47
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,372 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Unity Technologies.
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Unity.CodeEditor;
|
||||
|
||||
[assembly: InternalsVisibleTo("Unity.VisualStudio.EditorTests")]
|
||||
[assembly: InternalsVisibleTo("Unity.VisualStudio.Standalone.EditorTests")]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor
|
||||
{
|
||||
[InitializeOnLoad]
|
||||
public class VisualStudioEditor : IExternalCodeEditor
|
||||
{
|
||||
internal static bool IsOSX => Application.platform == RuntimePlatform.OSXEditor;
|
||||
internal static bool IsWindows => !IsOSX && Path.DirectorySeparatorChar == FileUtility.WinSeparator && Environment.NewLine == "\r\n";
|
||||
|
||||
CodeEditor.Installation[] IExternalCodeEditor.Installations => _discoverInstallations.Result
|
||||
.Select(i => i.ToCodeEditorInstallation())
|
||||
.ToArray();
|
||||
|
||||
private static readonly AsyncOperation<IVisualStudioInstallation[]> _discoverInstallations;
|
||||
|
||||
private readonly IGenerator _generator = new ProjectGeneration();
|
||||
|
||||
static VisualStudioEditor()
|
||||
{
|
||||
if (!UnityInstallation.IsMainUnityEditorProcess)
|
||||
return;
|
||||
|
||||
if (IsWindows)
|
||||
Discovery.FindVSWhere();
|
||||
|
||||
CodeEditor.Register(new VisualStudioEditor());
|
||||
|
||||
_discoverInstallations = AsyncOperation<IVisualStudioInstallation[]>.Run(DiscoverInstallations);
|
||||
}
|
||||
|
||||
private static IVisualStudioInstallation[] DiscoverInstallations()
|
||||
{
|
||||
try
|
||||
{
|
||||
return Discovery
|
||||
.GetVisualStudioInstallations()
|
||||
.ToArray();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"Error detecting Visual Studio installations: {ex}");
|
||||
return Array.Empty<IVisualStudioInstallation>();
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsEnabled => CodeEditor.CurrentEditor is VisualStudioEditor && UnityInstallation.IsMainUnityEditorProcess;
|
||||
|
||||
// this one seems legacy and not used anymore
|
||||
// keeping it for now given it is public, so we need a major bump to remove it
|
||||
public void CreateIfDoesntExist()
|
||||
{
|
||||
if (!_generator.HasSolutionBeenGenerated())
|
||||
_generator.Sync();
|
||||
}
|
||||
|
||||
public void Initialize(string editorInstallationPath)
|
||||
{
|
||||
}
|
||||
|
||||
internal virtual bool TryGetVisualStudioInstallationForPath(string editorPath, bool searchInstallations, out IVisualStudioInstallation installation)
|
||||
{
|
||||
if (searchInstallations)
|
||||
{
|
||||
// lookup for well known installations
|
||||
foreach (var candidate in _discoverInstallations.Result)
|
||||
{
|
||||
if (!string.Equals(Path.GetFullPath(editorPath), Path.GetFullPath(candidate.Path), StringComparison.OrdinalIgnoreCase))
|
||||
continue;
|
||||
|
||||
installation = candidate;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return Discovery.TryDiscoverInstallation(editorPath, out installation);
|
||||
}
|
||||
|
||||
public virtual bool TryGetInstallationForPath(string editorPath, out CodeEditor.Installation installation)
|
||||
{
|
||||
var result = TryGetVisualStudioInstallationForPath(editorPath, searchInstallations: false, out var vsi);
|
||||
installation = vsi == null ? default : vsi.ToCodeEditorInstallation();
|
||||
return result;
|
||||
}
|
||||
|
||||
public void OnGUI()
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
var package = UnityEditor.PackageManager.PackageInfo.FindForAssembly(GetType().Assembly);
|
||||
|
||||
var style = new GUIStyle
|
||||
{
|
||||
richText = true,
|
||||
margin = new RectOffset(0, 4, 0, 0)
|
||||
};
|
||||
|
||||
GUILayout.Label($"<size=10><color=grey>{package.displayName} v{package.version} enabled</color></size>", style);
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.LabelField("Generate .csproj files for:");
|
||||
EditorGUI.indentLevel++;
|
||||
SettingsButton(ProjectGenerationFlag.Embedded, "Embedded packages", "");
|
||||
SettingsButton(ProjectGenerationFlag.Local, "Local packages", "");
|
||||
SettingsButton(ProjectGenerationFlag.Registry, "Registry packages", "");
|
||||
SettingsButton(ProjectGenerationFlag.Git, "Git packages", "");
|
||||
SettingsButton(ProjectGenerationFlag.BuiltIn, "Built-in packages", "");
|
||||
SettingsButton(ProjectGenerationFlag.LocalTarBall, "Local tarball", "");
|
||||
SettingsButton(ProjectGenerationFlag.Unknown, "Packages from unknown sources", "");
|
||||
SettingsButton(ProjectGenerationFlag.PlayerAssemblies, "Player projects", "For each player project generate an additional csproj with the name 'project-player.csproj'");
|
||||
RegenerateProjectFiles();
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
void RegenerateProjectFiles()
|
||||
{
|
||||
var rect = EditorGUI.IndentedRect(EditorGUILayout.GetControlRect(new GUILayoutOption[] { }));
|
||||
rect.width = 252;
|
||||
if (GUI.Button(rect, "Regenerate project files"))
|
||||
{
|
||||
_generator.Sync();
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsButton(ProjectGenerationFlag preference, string guiMessage, string toolTip)
|
||||
{
|
||||
var prevValue = _generator.AssemblyNameProvider.ProjectGenerationFlag.HasFlag(preference);
|
||||
var newValue = EditorGUILayout.Toggle(new GUIContent(guiMessage, toolTip), prevValue);
|
||||
if (newValue != prevValue)
|
||||
{
|
||||
_generator.AssemblyNameProvider.ToggleProjectGeneration(preference);
|
||||
}
|
||||
}
|
||||
|
||||
public void SyncIfNeeded(string[] addedFiles, string[] deletedFiles, string[] movedFiles, string[] movedFromFiles, string[] importedFiles)
|
||||
{
|
||||
_generator.SyncIfNeeded(addedFiles.Union(deletedFiles).Union(movedFiles).Union(movedFromFiles), importedFiles);
|
||||
|
||||
foreach (var file in importedFiles.Where(a => Path.GetExtension(a) == ".pdb"))
|
||||
{
|
||||
var pdbFile = FileUtility.GetAssetFullPath(file);
|
||||
|
||||
// skip Unity packages like com.unity.ext.nunit
|
||||
if (pdbFile.IndexOf($"{Path.DirectorySeparatorChar}com.unity.", StringComparison.OrdinalIgnoreCase) > 0)
|
||||
continue;
|
||||
|
||||
var asmFile = Path.ChangeExtension(pdbFile, ".dll");
|
||||
if (!File.Exists(asmFile) || !Image.IsAssembly(asmFile))
|
||||
continue;
|
||||
|
||||
if (Symbols.IsPortableSymbolFile(pdbFile))
|
||||
continue;
|
||||
|
||||
UnityEngine.Debug.LogWarning($"Unity is only able to load mdb or portable-pdb symbols. {file} is using a legacy pdb format.");
|
||||
}
|
||||
}
|
||||
|
||||
public void SyncAll()
|
||||
{
|
||||
AssetDatabase.Refresh();
|
||||
_generator.Sync();
|
||||
}
|
||||
|
||||
bool IsSupportedPath(string path)
|
||||
{
|
||||
// Path is empty with "Open C# Project", as we only want to open the solution without specific files
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return true;
|
||||
|
||||
// cs, uxml, uss, shader, compute, cginc, hlsl, glslinc, template are part of Unity builtin extensions
|
||||
// txt, xml, fnt, cd are -often- par of Unity user extensions
|
||||
// asdmdef is mandatory included
|
||||
if (_generator.IsSupportedFile(path))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void CheckCurrentEditorInstallation()
|
||||
{
|
||||
var editorPath = CodeEditor.CurrentEditorInstallation;
|
||||
try
|
||||
{
|
||||
if (Discovery.TryDiscoverInstallation(editorPath, out _))
|
||||
return;
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
}
|
||||
|
||||
UnityEngine.Debug.LogWarning($"Visual Studio executable {editorPath} is not found. Please change your settings in Edit > Preferences > External Tools.");
|
||||
}
|
||||
|
||||
public bool OpenProject(string path, int line, int column)
|
||||
{
|
||||
CheckCurrentEditorInstallation();
|
||||
|
||||
if (!IsSupportedPath(path))
|
||||
return false;
|
||||
|
||||
if (!IsProjectGeneratedFor(path, out var missingFlag))
|
||||
UnityEngine.Debug.LogWarning($"You are trying to open {path} outside a generated project. This might cause problems with IntelliSense and debugging. To avoid this, you can change your .csproj preferences in Edit > Preferences > External Tools and enable {GetProjectGenerationFlagDescription(missingFlag)} generation.");
|
||||
|
||||
if (IsOSX)
|
||||
return OpenOSXApp(path, line, column);
|
||||
|
||||
if (IsWindows)
|
||||
return OpenWindowsApp(path, line);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string GetProjectGenerationFlagDescription(ProjectGenerationFlag flag)
|
||||
{
|
||||
switch (flag)
|
||||
{
|
||||
case ProjectGenerationFlag.BuiltIn:
|
||||
return "Built-in packages";
|
||||
case ProjectGenerationFlag.Embedded:
|
||||
return "Embedded packages";
|
||||
case ProjectGenerationFlag.Git:
|
||||
return "Git packages";
|
||||
case ProjectGenerationFlag.Local:
|
||||
return "Local packages";
|
||||
case ProjectGenerationFlag.LocalTarBall:
|
||||
return "Local tarball";
|
||||
case ProjectGenerationFlag.PlayerAssemblies:
|
||||
return "Player projects";
|
||||
case ProjectGenerationFlag.Registry:
|
||||
return "Registry packages";
|
||||
case ProjectGenerationFlag.Unknown:
|
||||
return "Packages from unknown sources";
|
||||
default:
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsProjectGeneratedFor(string path, out ProjectGenerationFlag missingFlag)
|
||||
{
|
||||
missingFlag = ProjectGenerationFlag.None;
|
||||
|
||||
// No need to check when opening the whole solution
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return true;
|
||||
|
||||
// We only want to check for cs scripts
|
||||
if (ProjectGeneration.ScriptingLanguageForFile(path) != ScriptingLanguage.CSharp)
|
||||
return true;
|
||||
|
||||
// Even on windows, the package manager requires relative path + unix style separators for queries
|
||||
var basePath = _generator.ProjectDirectory;
|
||||
var relativePath = FileUtility
|
||||
.NormalizeWindowsToUnix(path)
|
||||
.Replace(basePath, string.Empty)
|
||||
.Trim(FileUtility.UnixSeparator);
|
||||
|
||||
var packageInfo = UnityEditor.PackageManager.PackageInfo.FindForAssetPath(relativePath);
|
||||
if (packageInfo == null)
|
||||
return true;
|
||||
|
||||
var source = packageInfo.source;
|
||||
if (!Enum.TryParse<ProjectGenerationFlag>(source.ToString(), out var flag))
|
||||
return true;
|
||||
|
||||
if (_generator.AssemblyNameProvider.ProjectGenerationFlag.HasFlag(flag))
|
||||
return true;
|
||||
|
||||
// Return false if we found a source not flagged for generation
|
||||
missingFlag = flag;
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool OpenWindowsApp(string path, int line)
|
||||
{
|
||||
var progpath = FileUtility.GetPackageAssetFullPath("Editor", "COMIntegration", "Release", "COMIntegration.exe");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(progpath))
|
||||
return false;
|
||||
|
||||
string absolutePath = "";
|
||||
if (!string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
absolutePath = Path.GetFullPath(path);
|
||||
}
|
||||
|
||||
// We remove all invalid chars from the solution filename, but we cannot prevent the user from using a specific path for the Unity project
|
||||
// So process the fullpath to make it compatible with VS
|
||||
var solution = GetOrGenerateSolutionFile(path);
|
||||
if (!string.IsNullOrWhiteSpace(solution))
|
||||
{
|
||||
solution = $"\"{solution}\"";
|
||||
solution = solution.Replace("^", "^^");
|
||||
}
|
||||
|
||||
var process = new Process
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = progpath,
|
||||
Arguments = $"\"{CodeEditor.CurrentEditorInstallation}\" {solution} \"{absolutePath}\" {line}",
|
||||
CreateNoWindow = true,
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
}
|
||||
};
|
||||
var result = process.Start();
|
||||
|
||||
while (!process.StandardOutput.EndOfStream)
|
||||
{
|
||||
var outputLine = process.StandardOutput.ReadLine();
|
||||
if (outputLine == "displayProgressBar")
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("Opening Visual Studio", "Starting up Visual Studio, this might take some time.", .5f);
|
||||
}
|
||||
|
||||
if (outputLine == "clearprogressbar")
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
}
|
||||
|
||||
var errorOutput = process.StandardError.ReadToEnd();
|
||||
if (!string.IsNullOrEmpty(errorOutput))
|
||||
{
|
||||
Console.WriteLine("Error: \n" + errorOutput);
|
||||
}
|
||||
|
||||
process.WaitForExit();
|
||||
return result;
|
||||
}
|
||||
|
||||
[DllImport("AppleEventIntegration")]
|
||||
static extern bool OpenVisualStudio(string appPath, string solutionPath, string filePath, int line);
|
||||
|
||||
bool OpenOSXApp(string path, int line, int column)
|
||||
{
|
||||
string absolutePath = "";
|
||||
if (!string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
absolutePath = Path.GetFullPath(path);
|
||||
}
|
||||
|
||||
var solution = GetOrGenerateSolutionFile(path);
|
||||
return OpenVisualStudio(CodeEditor.CurrentEditorInstallation, solution, absolutePath, line);
|
||||
}
|
||||
|
||||
private string GetOrGenerateSolutionFile(string path)
|
||||
{
|
||||
_generator.Sync();
|
||||
return _generator.SolutionFile();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0173aff8c07e06b42af07ebdd7f08032
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,210 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.Win32;
|
||||
using Unity.CodeEditor;
|
||||
using IOPath = System.IO.Path;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor
|
||||
{
|
||||
internal interface IVisualStudioInstallation
|
||||
{
|
||||
string Path { get; }
|
||||
bool SupportsAnalyzers { get; }
|
||||
Version LatestLanguageVersionSupported { get; }
|
||||
string[] GetAnalyzers();
|
||||
CodeEditor.Installation ToCodeEditorInstallation();
|
||||
}
|
||||
|
||||
internal class VisualStudioInstallation : IVisualStudioInstallation
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Path { get; set; }
|
||||
public Version Version { get; set; }
|
||||
public bool IsPrerelease { get; set; }
|
||||
|
||||
public bool SupportsAnalyzers
|
||||
{
|
||||
get
|
||||
{
|
||||
if (VisualStudioEditor.IsWindows)
|
||||
return Version >= new Version(16, 3);
|
||||
|
||||
if (VisualStudioEditor.IsOSX)
|
||||
return Version >= new Version(8, 3);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// C# language version support for Visual Studio
|
||||
private static VersionPair[] WindowsVersionTable =
|
||||
{
|
||||
// VisualStudio 2019
|
||||
new VersionPair(16,8, /* => */ 9,0),
|
||||
new VersionPair(16,0, /* => */ 8,0),
|
||||
|
||||
// VisualStudio 2017
|
||||
new VersionPair(15,7, /* => */ 7,3),
|
||||
new VersionPair(15,5, /* => */ 7,2),
|
||||
new VersionPair(15,3, /* => */ 7,1),
|
||||
new VersionPair(15,0, /* => */ 7,0),
|
||||
};
|
||||
|
||||
// C# language version support for Visual Studio for Mac
|
||||
private static VersionPair[] OSXVersionTable =
|
||||
{
|
||||
// VisualStudio for Mac 8.x
|
||||
new VersionPair(8,8, /* => */ 9,0),
|
||||
new VersionPair(8,3, /* => */ 8,0),
|
||||
new VersionPair(8,0, /* => */ 7,3),
|
||||
};
|
||||
|
||||
public Version LatestLanguageVersionSupported
|
||||
{
|
||||
get
|
||||
{
|
||||
VersionPair[] versions = null;
|
||||
|
||||
if (VisualStudioEditor.IsWindows)
|
||||
versions = WindowsVersionTable;
|
||||
|
||||
if (VisualStudioEditor.IsOSX)
|
||||
versions = OSXVersionTable;
|
||||
|
||||
if (versions != null)
|
||||
{
|
||||
foreach (var entry in versions)
|
||||
{
|
||||
if (Version >= entry.IdeVersion)
|
||||
return entry.LanguageVersion;
|
||||
}
|
||||
}
|
||||
|
||||
// default to 7.0 given we support at least VS 2017
|
||||
return new Version(7, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static string ReadRegistry(RegistryKey hive, string keyName, string valueName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var unitykey = hive.OpenSubKey(keyName);
|
||||
|
||||
var result = (string)unitykey?.GetValue(valueName);
|
||||
return result;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetWindowsBridgeFromRegistry()
|
||||
{
|
||||
var keyName = $"Software\\Microsoft\\Microsoft Visual Studio {Version.Major}.0 Tools for Unity";
|
||||
const string valueName = "UnityExtensionPath";
|
||||
|
||||
var bridge = ReadRegistry(Registry.CurrentUser, keyName, valueName);
|
||||
if (string.IsNullOrEmpty(bridge))
|
||||
bridge = ReadRegistry(Registry.LocalMachine, keyName, valueName);
|
||||
|
||||
return bridge;
|
||||
}
|
||||
|
||||
// We only use this to find analyzers, we do not need to load this assembly anymore
|
||||
private string GetExtensionPath()
|
||||
{
|
||||
if (VisualStudioEditor.IsWindows)
|
||||
{
|
||||
const string extensionName = "Visual Studio Tools for Unity";
|
||||
const string extensionAssembly = "SyntaxTree.VisualStudio.Unity.dll";
|
||||
|
||||
var vsDirectory = IOPath.GetDirectoryName(Path);
|
||||
var vstuDirectory = IOPath.Combine(vsDirectory, "Extensions", "Microsoft", extensionName);
|
||||
|
||||
if (File.Exists(IOPath.Combine(vstuDirectory, extensionAssembly)))
|
||||
return vstuDirectory;
|
||||
}
|
||||
|
||||
if (VisualStudioEditor.IsOSX)
|
||||
{
|
||||
const string addinName = "MonoDevelop.Unity";
|
||||
const string addinAssembly = addinName + ".dll";
|
||||
|
||||
// user addins repository
|
||||
var localAddins = IOPath.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.Personal),
|
||||
$"Library/Application Support/VisualStudio/${Version.Major}.0" + "/LocalInstall/Addins");
|
||||
|
||||
// In the user addins repository, the addins are suffixed by their versions, like `MonoDevelop.Unity.1.0`
|
||||
// When installing another local user addin, MD will remove files inside the folder
|
||||
// So we browse all VSTUM addins, and return the one with an addin assembly
|
||||
if (Directory.Exists(localAddins))
|
||||
{
|
||||
foreach (var folder in Directory.GetDirectories(localAddins, addinName + "*", SearchOption.TopDirectoryOnly))
|
||||
{
|
||||
if (File.Exists(IOPath.Combine(folder, addinAssembly)))
|
||||
return folder;
|
||||
}
|
||||
}
|
||||
|
||||
// Check in Visual Studio.app/
|
||||
// In that case the name of the addin is used
|
||||
var addinPath = IOPath.Combine(Path, $"Contents/Resources/lib/monodevelop/AddIns/{addinName}");
|
||||
if (File.Exists(IOPath.Combine(addinPath, addinAssembly)))
|
||||
return addinPath;
|
||||
|
||||
addinPath = IOPath.Combine(Path, $"Contents/MonoBundle/Addins/{addinName}");
|
||||
if (File.Exists(IOPath.Combine(addinPath, addinAssembly)))
|
||||
return addinPath;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string[] GetAnalyzers(string path)
|
||||
{
|
||||
var analyzersDirectory = IOPath.GetFullPath(IOPath.Combine(path, "Analyzers"));
|
||||
|
||||
if (Directory.Exists(analyzersDirectory))
|
||||
return Directory.GetFiles(analyzersDirectory, "*Analyzers.dll", SearchOption.AllDirectories);
|
||||
|
||||
return Array.Empty<string>();
|
||||
}
|
||||
|
||||
public string[] GetAnalyzers()
|
||||
{
|
||||
var vstuPath = GetExtensionPath();
|
||||
if (string.IsNullOrEmpty(vstuPath))
|
||||
return Array.Empty<string>();
|
||||
|
||||
if (VisualStudioEditor.IsOSX)
|
||||
return GetAnalyzers(vstuPath);
|
||||
|
||||
if (VisualStudioEditor.IsWindows)
|
||||
{
|
||||
var analyzers = GetAnalyzers(vstuPath);
|
||||
if (analyzers?.Length > 0)
|
||||
return analyzers;
|
||||
|
||||
var bridge = GetWindowsBridgeFromRegistry();
|
||||
if (File.Exists(bridge))
|
||||
return GetAnalyzers(IOPath.Combine(IOPath.GetDirectoryName(bridge), ".."));
|
||||
}
|
||||
|
||||
// Local assets
|
||||
// return FileUtility.FindPackageAssetFullPath("Analyzers a:packages", ".Analyzers.dll");
|
||||
return Array.Empty<string>();
|
||||
}
|
||||
|
||||
public CodeEditor.Installation ToCodeEditorInstallation()
|
||||
{
|
||||
return new CodeEditor.Installation() { Name = Name, Path = Path };
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bb86eea06f54fb24caa7046a8a764945
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,291 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using Microsoft.Unity.VisualStudio.Editor.Messaging;
|
||||
using Microsoft.Unity.VisualStudio.Editor.Testing;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using MessageType = Microsoft.Unity.VisualStudio.Editor.Messaging.MessageType;
|
||||
|
||||
namespace Microsoft.Unity.VisualStudio.Editor
|
||||
{
|
||||
[InitializeOnLoad]
|
||||
internal class VisualStudioIntegration
|
||||
{
|
||||
class Client
|
||||
{
|
||||
public IPEndPoint EndPoint { get; set; }
|
||||
public DateTime LastMessage { get; set; }
|
||||
}
|
||||
|
||||
private static Messager _messager;
|
||||
|
||||
private static readonly Queue<Message> _incoming = new Queue<Message>();
|
||||
private static readonly Dictionary<IPEndPoint, Client> _clients = new Dictionary<IPEndPoint, Client>();
|
||||
private static readonly object _incomingLock = new object();
|
||||
private static readonly object _clientsLock = new object();
|
||||
|
||||
static VisualStudioIntegration()
|
||||
{
|
||||
if (!VisualStudioEditor.IsEnabled)
|
||||
return;
|
||||
|
||||
RunOnceOnUpdate(() =>
|
||||
{
|
||||
// Despite using ReuseAddress|!ExclusiveAddressUse, we can fail here:
|
||||
// - if another application is using this port with exclusive access
|
||||
// - or if the firewall is not properly configured
|
||||
var messagingPort = MessagingPort();
|
||||
|
||||
try
|
||||
{
|
||||
_messager = Messager.BindTo(messagingPort);
|
||||
_messager.ReceiveMessage += ReceiveMessage;
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
// We'll have a chance to try to rebind on next domain reload
|
||||
Debug.LogWarning($"Unable to use UDP port {messagingPort} for VS/Unity messaging. You should check if another process is already bound to this port or if your firewall settings are compatible.");
|
||||
}
|
||||
|
||||
RunOnShutdown(Shutdown);
|
||||
});
|
||||
|
||||
EditorApplication.update += OnUpdate;
|
||||
|
||||
CheckLegacyAssemblies();
|
||||
}
|
||||
|
||||
private static void CheckLegacyAssemblies()
|
||||
{
|
||||
var checkList = new HashSet<string>(new[] { KnownAssemblies.UnityVS, KnownAssemblies.Messaging, KnownAssemblies.Bridge });
|
||||
|
||||
try
|
||||
{
|
||||
var assemblies = AppDomain
|
||||
.CurrentDomain
|
||||
.GetAssemblies()
|
||||
.Where(a => checkList.Contains(a.GetName().Name));
|
||||
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
// for now we only want to warn against local assemblies, do not check externals.
|
||||
var relativePath = FileUtility.MakeRelativeToProjectPath(assembly.Location);
|
||||
if (relativePath == null)
|
||||
continue;
|
||||
|
||||
Debug.LogWarning($"Project contains legacy assembly that could interfere with the Visual Studio Package. You should delete {relativePath}");
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// abandon legacy check
|
||||
}
|
||||
}
|
||||
|
||||
private static void RunOnceOnUpdate(Action action)
|
||||
{
|
||||
var callback = null as EditorApplication.CallbackFunction;
|
||||
|
||||
callback = () =>
|
||||
{
|
||||
EditorApplication.update -= callback;
|
||||
action();
|
||||
};
|
||||
|
||||
EditorApplication.update += callback;
|
||||
}
|
||||
|
||||
private static void RunOnShutdown(Action action)
|
||||
{
|
||||
// Mono on OSX has all kinds of quirks on AppDomain shutdown
|
||||
if (!VisualStudioEditor.IsWindows)
|
||||
return;
|
||||
|
||||
AppDomain.CurrentDomain.DomainUnload += (_, __) => action();
|
||||
}
|
||||
|
||||
private static int DebuggingPort()
|
||||
{
|
||||
return 56000 + (System.Diagnostics.Process.GetCurrentProcess().Id % 1000);
|
||||
}
|
||||
|
||||
private static int MessagingPort()
|
||||
{
|
||||
return DebuggingPort() + 2;
|
||||
}
|
||||
|
||||
private static void ReceiveMessage(object sender, MessageEventArgs args)
|
||||
{
|
||||
OnMessage(args.Message);
|
||||
}
|
||||
|
||||
private static void OnUpdate()
|
||||
{
|
||||
lock (_incomingLock)
|
||||
{
|
||||
while (_incoming.Count > 0)
|
||||
{
|
||||
ProcessIncoming(_incoming.Dequeue());
|
||||
}
|
||||
}
|
||||
|
||||
lock (_clientsLock)
|
||||
{
|
||||
foreach (var client in _clients.Values.ToArray())
|
||||
{
|
||||
if (DateTime.Now.Subtract(client.LastMessage) > TimeSpan.FromMilliseconds(4000))
|
||||
_clients.Remove(client.EndPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddMessage(Message message)
|
||||
{
|
||||
lock (_incomingLock)
|
||||
{
|
||||
_incoming.Enqueue(message);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ProcessIncoming(Message message)
|
||||
{
|
||||
lock (_clientsLock)
|
||||
{
|
||||
CheckClient(message);
|
||||
}
|
||||
|
||||
switch (message.Type)
|
||||
{
|
||||
case MessageType.Ping:
|
||||
Answer(message, MessageType.Pong);
|
||||
break;
|
||||
case MessageType.Play:
|
||||
Shutdown();
|
||||
EditorApplication.isPlaying = true;
|
||||
break;
|
||||
case MessageType.Stop:
|
||||
EditorApplication.isPlaying = false;
|
||||
break;
|
||||
case MessageType.Pause:
|
||||
EditorApplication.isPaused = true;
|
||||
break;
|
||||
case MessageType.Unpause:
|
||||
EditorApplication.isPaused = false;
|
||||
break;
|
||||
case MessageType.Build:
|
||||
// Not used anymore
|
||||
break;
|
||||
case MessageType.Refresh:
|
||||
Refresh();
|
||||
break;
|
||||
case MessageType.Version:
|
||||
Answer(message, MessageType.Version, PackageVersion());
|
||||
break;
|
||||
case MessageType.UpdatePackage:
|
||||
// Not used anymore
|
||||
break;
|
||||
case MessageType.ProjectPath:
|
||||
Answer(message, MessageType.ProjectPath, Path.GetFullPath(Path.Combine(Application.dataPath, "..")));
|
||||
break;
|
||||
case MessageType.ExecuteTests:
|
||||
TestRunnerApiListener.ExecuteTests(message.Value);
|
||||
break;
|
||||
case MessageType.RetrieveTestList:
|
||||
TestRunnerApiListener.RetrieveTestList(message.Value);
|
||||
break;
|
||||
case MessageType.ShowUsage:
|
||||
UsageUtility.ShowUsage(message.Value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void CheckClient(Message message)
|
||||
{
|
||||
var endPoint = message.Origin;
|
||||
|
||||
if (!_clients.TryGetValue(endPoint, out var client))
|
||||
{
|
||||
client = new Client
|
||||
{
|
||||
EndPoint = endPoint,
|
||||
LastMessage = DateTime.Now
|
||||
};
|
||||
|
||||
_clients.Add(endPoint, client);
|
||||
}
|
||||
else
|
||||
{
|
||||
client.LastMessage = DateTime.Now;
|
||||
}
|
||||
}
|
||||
|
||||
internal static string PackageVersion()
|
||||
{
|
||||
var package = UnityEditor.PackageManager.PackageInfo.FindForAssembly(typeof(VisualStudioIntegration).Assembly);
|
||||
return package.version;
|
||||
}
|
||||
|
||||
private static void Refresh()
|
||||
{
|
||||
// If the user disabled auto-refresh in Unity, do not try to force refresh the Asset database
|
||||
if (!EditorPrefs.GetBool("kAutoRefresh", true))
|
||||
return;
|
||||
|
||||
RunOnceOnUpdate(AssetDatabase.Refresh);
|
||||
}
|
||||
|
||||
private static void OnMessage(Message message)
|
||||
{
|
||||
AddMessage(message);
|
||||
}
|
||||
|
||||
private static void Answer(Client client, MessageType answerType, string answerValue)
|
||||
{
|
||||
Answer(client.EndPoint, answerType, answerValue);
|
||||
}
|
||||
|
||||
private static void Answer(Message message, MessageType answerType, string answerValue = "")
|
||||
{
|
||||
var targetEndPoint = message.Origin;
|
||||
|
||||
Answer(
|
||||
targetEndPoint,
|
||||
answerType,
|
||||
answerValue);
|
||||
}
|
||||
|
||||
private static void Answer(IPEndPoint targetEndPoint, MessageType answerType, string answerValue)
|
||||
{
|
||||
_messager?.SendMessage(targetEndPoint, answerType, answerValue);
|
||||
}
|
||||
|
||||
private static void Shutdown()
|
||||
{
|
||||
if (_messager == null)
|
||||
return;
|
||||
|
||||
_messager.ReceiveMessage -= ReceiveMessage;
|
||||
_messager.Dispose();
|
||||
_messager = null;
|
||||
}
|
||||
|
||||
internal static void BroadcastMessage(MessageType type, string value)
|
||||
{
|
||||
lock (_clientsLock)
|
||||
{
|
||||
foreach (var client in _clients.Values.ToArray())
|
||||
{
|
||||
Answer(client, type, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 48fcd6ebd5ce8fd4cbe931895233677d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue