Init
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
#if !QC_DISABLED && !QC_DISABLE_BUILTIN_ALL && !QC_DISABLE_BUILTIN_EXTRA
|
||||
using UnityEngine;
|
||||
|
||||
namespace QFSW.QC.Extras
|
||||
{
|
||||
public static class ApplicationCommands
|
||||
{
|
||||
[Command("quit", "Quits the player application")]
|
||||
[CommandPlatform(Platform.AllPlatforms ^ (Platform.EditorPlatforms | Platform.WebGLPlayer))]
|
||||
private static void Quit()
|
||||
{
|
||||
Application.Quit();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e9da7f85fdcb033409727f504fdda906
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,26 @@
|
||||
#if !QC_DISABLED && !QC_DISABLE_BUILTIN_ALL && !QC_DISABLE_BUILTIN_EXTRA
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QFSW.QC.Extras
|
||||
{
|
||||
[AddComponentMenu("")]
|
||||
public class CoroutineCommands : MonoBehaviour
|
||||
{
|
||||
[Command("start-coroutine", "starts the supplied command as a coroutine", MonoTargetType.Singleton)]
|
||||
private void StartCoroutineCommand(string coroutineCommand)
|
||||
{
|
||||
object coroutineReturn = QuantumConsoleProcessor.InvokeCommand(coroutineCommand);
|
||||
if (coroutineReturn is IEnumerator)
|
||||
{
|
||||
StartCoroutine(coroutineReturn as IEnumerator);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException($"{coroutineCommand} is not a coroutine");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1a0d171dbb1da05489c852d95745226f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,115 @@
|
||||
#if UNITY_EDITOR && !QC_DISABLED && !QC_DISABLE_BUILTIN_ALL && !QC_DISABLE_BUILTIN_EXTRA
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QFSW.QC.Extras
|
||||
{
|
||||
internal static class EditorCommands
|
||||
{
|
||||
private static IEnumerable<GameObject> LoadPrefabs(string prefabName, params PrefabAssetType[] prefabTypes)
|
||||
{
|
||||
string filter = $"{prefabName} t:GameObject";
|
||||
string[] guids = AssetDatabase.FindAssets(filter);
|
||||
|
||||
foreach (string guid in guids)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
GameObject obj = AssetDatabase.LoadAssetAtPath<GameObject>(path);
|
||||
|
||||
if (obj.name == prefabName)
|
||||
{
|
||||
if (prefabTypes.Contains(PrefabUtility.GetPrefabAssetType(obj)))
|
||||
{
|
||||
yield return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static T ForceSingle<T>(IEnumerable<T> stream, string errorMessage, string warningMessage)
|
||||
{
|
||||
bool singleFound = false;
|
||||
T single = default;
|
||||
foreach (T item in stream)
|
||||
{
|
||||
if (singleFound)
|
||||
{
|
||||
Debug.LogWarning(warningMessage);
|
||||
break;
|
||||
}
|
||||
|
||||
single = item;
|
||||
singleFound = true;
|
||||
}
|
||||
|
||||
if (singleFound)
|
||||
{
|
||||
return single;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private static GameObject LoadPrefab(string prefabName)
|
||||
{
|
||||
IEnumerable<GameObject> prefabs = LoadPrefabs(prefabName, PrefabAssetType.Regular, PrefabAssetType.Variant);
|
||||
return ForceSingle(prefabs, $"No prefab with the name {prefabName} could be found.", $"Multiple prefabs with the name {prefabName} were found");
|
||||
}
|
||||
|
||||
private static GameObject LoadModel(string modelName)
|
||||
{
|
||||
IEnumerable<GameObject> models = LoadPrefabs(modelName, PrefabAssetType.Model);
|
||||
return ForceSingle(models, $"No model with the name {modelName} could be found.", $"Multiple models with the name {modelName} were found");
|
||||
}
|
||||
|
||||
[Command("instantiate-prefab", "Instantiates a GameObject from the specified prefab", Platform.EditorPlatforms)]
|
||||
private static void InstantiateGOFromPrefab(
|
||||
[CommandParameterDescription("The name of the prefab to instantiate a copy of.")]string prefabName,
|
||||
[CommandParameterDescription("The position of the instantiated GameObject.")]Vector3 position,
|
||||
[CommandParameterDescription("The rotation of the instantiated GameObject.")]Quaternion rotation)
|
||||
{
|
||||
GameObject.Instantiate(LoadPrefab(prefabName), position, rotation);
|
||||
}
|
||||
|
||||
[Command("instantiate-prefab", "Instantiates a GameObject from the specified prefab", Platform.EditorPlatforms)]
|
||||
private static void InstantiateGOFromPrefab(string prefabName, Vector3 position)
|
||||
{
|
||||
GameObject prefab = LoadPrefab(prefabName);
|
||||
GameObject.Instantiate(prefab, position, prefab.transform.rotation);
|
||||
}
|
||||
|
||||
[Command("instantiate-prefab", "Instantiates a GameObject from the specified prefab", Platform.EditorPlatforms)]
|
||||
private static void InstantiateGOFromPrefab(string prefabName)
|
||||
{
|
||||
GameObject.Instantiate(LoadPrefab(prefabName));
|
||||
}
|
||||
|
||||
[Command("instantiate-model", "Instantiates a GameObject from the specified model prefab", Platform.EditorPlatforms)]
|
||||
private static void InstantiateGOFromModelPrefab(
|
||||
[CommandParameterDescription("The name of the model to instantiate a copy of.")]string modelName,
|
||||
[CommandParameterDescription("The position of the instantiated GameObject.")]Vector3 position,
|
||||
[CommandParameterDescription("The rotation of the instantiated GameObject.")]Quaternion rotation)
|
||||
{
|
||||
GameObject.Instantiate(LoadModel(modelName), position, rotation);
|
||||
}
|
||||
|
||||
[Command("instantiate-model", "Instantiates a GameObject from the specified model prefab", Platform.EditorPlatforms)]
|
||||
private static void InstantiateGOFromModelPrefab(string modelName, Vector3 position)
|
||||
{
|
||||
GameObject prefab = LoadModel(modelName);
|
||||
GameObject.Instantiate(prefab, position, prefab.transform.rotation);
|
||||
}
|
||||
|
||||
[Command("instantiate-model", "Instantiates a GameObject from the specified model prefab", Platform.EditorPlatforms)]
|
||||
private static void InstantiateGOFromModelPrefab(string modelName)
|
||||
{
|
||||
GameObject.Instantiate(LoadModel(modelName));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 15cc172f9ab7e744ebb12f0ac6965c79
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,33 @@
|
||||
#if !QC_DISABLED && !QC_DISABLE_BUILTIN_ALL && !QC_DISABLE_BUILTIN_EXTRA
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace QFSW.QC.Extras
|
||||
{
|
||||
public static class FileCommands
|
||||
{
|
||||
[Command("write-file", Platform.AllPlatforms ^ Platform.WebGLPlayer)]
|
||||
[CommandDescription("Writes the provided data to a file at the provided path")]
|
||||
private static async Task WriteFile(string path, string data)
|
||||
{
|
||||
FileInfo file = new FileInfo(path);
|
||||
file.Directory?.Create();
|
||||
|
||||
using (StreamWriter writer = new StreamWriter(path))
|
||||
{
|
||||
await writer.WriteAsync(data);
|
||||
}
|
||||
}
|
||||
|
||||
[Command("read-file", Platform.AllPlatforms ^ Platform.WebGLPlayer)]
|
||||
[CommandDescription("Reads the contents of the file at the provided path")]
|
||||
private static string ReadFile(string path)
|
||||
{
|
||||
using (StreamReader reader = new StreamReader(path))
|
||||
{
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4e0016aee4d4b3348a733339a47a9bab
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,30 @@
|
||||
#if !QC_DISABLED && !QC_DISABLE_BUILTIN_ALL && !QC_DISABLE_BUILTIN_EXTRA
|
||||
using UnityEngine;
|
||||
|
||||
namespace QFSW.QC.Extras
|
||||
{
|
||||
public static class GraphicsCommands
|
||||
{
|
||||
[Command("max-fps", "the maximum FPS imposed on the application. Set to -1 for unlimited.")]
|
||||
private static int MaxFPS
|
||||
{
|
||||
get => Application.targetFrameRate;
|
||||
set => Application.targetFrameRate = value;
|
||||
}
|
||||
|
||||
[Command("vsync", "enables or disables vsync for the application.")]
|
||||
private static bool VSync
|
||||
{
|
||||
get => QualitySettings.vSyncCount > 0;
|
||||
set => QualitySettings.vSyncCount = value ? 1 : 0;
|
||||
}
|
||||
|
||||
[Command("msaa", "Gets or sets the number of msaa samples in use. Valid values are 0, 2, 4 and 8.")]
|
||||
private static int MSAA
|
||||
{
|
||||
get => QualitySettings.antiAliasing;
|
||||
set => QualitySettings.antiAliasing = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f64c9104d262446583f03a81c2947fe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,50 @@
|
||||
#if !QC_DISABLED && !QC_DISABLE_BUILTIN_ALL && !QC_DISABLE_BUILTIN_EXTRA
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace QFSW.QC.Extras
|
||||
{
|
||||
[CommandPrefix("http.")]
|
||||
public static class HttpCommands
|
||||
{
|
||||
private static readonly HttpClient _client = new HttpClient();
|
||||
|
||||
[Command("get", "Sends a GET request to the specified URL.")]
|
||||
private static async Task<string> Get(string url)
|
||||
{
|
||||
HttpResponseMessage response = await _client.GetAsync(url);
|
||||
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
|
||||
[Command("delete", "Sends a DELETE request to the specified URL.")]
|
||||
private static async Task<string> Delete(string url)
|
||||
{
|
||||
HttpResponseMessage response = await _client.DeleteAsync(url);
|
||||
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
|
||||
[Command("post", "Sends a POST request to the specified URL. " +
|
||||
"A body may be sent with the request, with a default mediaType of text/plain.")]
|
||||
private static async Task<string> Post(string url, string content = "", string mediaType = "text/plain")
|
||||
{
|
||||
HttpContent body = new StringContent(content, Encoding.Default, mediaType);
|
||||
HttpResponseMessage response = await _client.PostAsync(url, body);
|
||||
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
|
||||
[Command("put", "Sends a PUT request to the specified URL. " +
|
||||
"A body may be sent with the request, with a default mediaType of text/plain.")]
|
||||
private static async Task<string> Put(string url, string content = "", string mediaType = "text/plain")
|
||||
{
|
||||
HttpContent body = new StringContent(content, Encoding.Default, mediaType);
|
||||
HttpResponseMessage response = await _client.PutAsync(url, body);
|
||||
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bc59abfc03e45b542947d9dc45bd1e72
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,100 @@
|
||||
#if !QC_DISABLED && !QC_DISABLE_BUILTIN_ALL && !QC_DISABLE_BUILTIN_EXTRA
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QFSW.QC.Extras
|
||||
{
|
||||
public class KeyBinderModule : MonoBehaviour
|
||||
{
|
||||
private readonly struct Binding
|
||||
{
|
||||
public readonly KeyCode Key;
|
||||
public readonly string Command;
|
||||
|
||||
public Binding(KeyCode key, string command)
|
||||
{
|
||||
Key = key;
|
||||
Command = command;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<Binding> _bindings = new List<Binding>();
|
||||
private QuantumConsole _consoleInstance;
|
||||
private bool _blocked = false;
|
||||
|
||||
private void BlockInput() { _blocked = true; }
|
||||
private void UnblockInput() { _blocked = false; }
|
||||
|
||||
private void BindToConsoleInstance()
|
||||
{
|
||||
if (!_consoleInstance) { _consoleInstance = FindObjectOfType<QuantumConsole>(); }
|
||||
if (_consoleInstance)
|
||||
{
|
||||
_consoleInstance.OnActivate += BlockInput;
|
||||
_consoleInstance.OnDeactivate += UnblockInput;
|
||||
|
||||
_blocked = _consoleInstance.IsActive;
|
||||
}
|
||||
else
|
||||
{
|
||||
UnblockInput();
|
||||
}
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
BindToConsoleInstance();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!_blocked)
|
||||
{
|
||||
foreach (Binding binding in _bindings)
|
||||
{
|
||||
if (InputHelper.GetKeyDown(binding.Key))
|
||||
{
|
||||
try
|
||||
{
|
||||
QuantumConsoleProcessor.InvokeCommand(binding.Command);
|
||||
}
|
||||
catch (System.Exception e) { Debug.LogException(e); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Command("bind", MonoTargetType.Singleton)]
|
||||
[CommandDescription("Binds a given command to a given key, so that every time the key is pressed, the command is invoked.")]
|
||||
private void AddBinding(KeyCode key, string command)
|
||||
{
|
||||
_bindings.Add(new Binding(key, command));
|
||||
}
|
||||
|
||||
[Command("unbind", MonoTargetType.Singleton)]
|
||||
[CommandDescription("Removes every binding for the given key")]
|
||||
private void RemoveBindings(KeyCode key)
|
||||
{
|
||||
_bindings.RemoveAll(x => x.Key == key);
|
||||
}
|
||||
|
||||
[Command("unbind-all", MonoTargetType.Singleton)]
|
||||
[CommandDescription("Unbinds every existing key binding")]
|
||||
private void RemoveAllBindings()
|
||||
{
|
||||
_bindings.Clear();
|
||||
}
|
||||
|
||||
[Command("display-bindings", MonoTargetType.Singleton)]
|
||||
[CommandDescription("Displays all existing bindings on the key binder")]
|
||||
private IEnumerable<object> DisplayAllBindings()
|
||||
{
|
||||
foreach (Binding binding in _bindings.OrderBy(x => x.Key))
|
||||
{
|
||||
yield return new KeyValuePair<KeyCode, string>(binding.Key, binding.Command);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb5d29f1db8b3ac41bea9c4355331a1f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,251 @@
|
||||
#if !QC_DISABLED && !QC_DISABLE_BUILTIN_ALL && !QC_DISABLE_BUILTIN_EXTRA
|
||||
using QFSW.QC.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace QFSW.QC.Extras
|
||||
{
|
||||
public static class MegaCommands
|
||||
{
|
||||
private static readonly QuantumSerializer Serializer = new QuantumSerializer();
|
||||
private static readonly QuantumParser Parser = new QuantumParser();
|
||||
|
||||
private static MethodInfo[] ExtractMethods(Type type, string name)
|
||||
{
|
||||
const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod |
|
||||
BindingFlags.Static | BindingFlags.Instance | BindingFlags.FlattenHierarchy;
|
||||
|
||||
MethodInfo[] methods = type.GetMethods(flags).Where(x => x.Name == name).ToArray();
|
||||
if (!methods.Any())
|
||||
{
|
||||
PropertyInfo property = type.GetProperty(name, flags);
|
||||
if (property != null)
|
||||
{
|
||||
methods = new[] {property.GetMethod, property.SetMethod}.Where(x => x != null).ToArray();
|
||||
if (methods.Length > 0)
|
||||
{
|
||||
return methods;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ArgumentException($"No method or property named {name} could be found in class {Serializer.SerializeFormatted(type)}");
|
||||
}
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
private static string GenerateSignature(MethodInfo method)
|
||||
{
|
||||
IEnumerable<string> paramParts = method.GetParameters()
|
||||
.Select(x => (x.Name, x.ParameterType))
|
||||
.Select(x => $"{x.ParameterType.GetDisplayName()} {x.Name}");
|
||||
|
||||
string paramSignature = string.Join(", ", paramParts);
|
||||
return $"{method.Name}({paramSignature})";
|
||||
}
|
||||
|
||||
private static MethodInfo GetIdealOverload(MethodInfo[] methods, bool isStatic, int argc)
|
||||
{
|
||||
methods = methods.Where(x => x.IsStatic == isStatic).ToArray();
|
||||
|
||||
if (methods.Length == 0)
|
||||
{
|
||||
throw new ArgumentException($"No {(isStatic ? "static" : "non-static")} overloads could be found.");
|
||||
}
|
||||
|
||||
if (methods.Length == 1)
|
||||
{
|
||||
return methods[0];
|
||||
}
|
||||
|
||||
methods = methods.Where(x => !x.IsGenericMethod).ToArray();
|
||||
if (methods.Length == 0)
|
||||
{
|
||||
throw new ArgumentException("Generic methods are not supported.");
|
||||
}
|
||||
|
||||
MethodInfo[] argcMatches = methods.Where(x => x.GetParameters().Length == argc).ToArray();
|
||||
if (argcMatches.Length == 1)
|
||||
{
|
||||
return argcMatches[0];
|
||||
}
|
||||
else if (argcMatches.Length == 0)
|
||||
{
|
||||
IEnumerable<string> signatures = methods.Select(GenerateSignature);
|
||||
string combinedSignatures = string.Join("\n", signatures);
|
||||
throw new ArgumentException($"No overloads with {argc} arguments were found. the following overloads are available:\n{combinedSignatures}");
|
||||
}
|
||||
else
|
||||
{
|
||||
IEnumerable<string> signatures = argcMatches.Select(GenerateSignature);
|
||||
string combinedSignatures = string.Join("\n", signatures);
|
||||
throw new ArgumentException($"Multiple overloads with the same argument count were found: please specify the types explicitly.\n{combinedSignatures}");
|
||||
}
|
||||
}
|
||||
|
||||
private static MethodInfo GetIdealOverload(MethodInfo[] methods, bool isStatic, Type[] argTypes)
|
||||
{
|
||||
// Exact matching
|
||||
foreach (MethodInfo method in methods)
|
||||
{
|
||||
if (method.IsStatic == isStatic)
|
||||
{
|
||||
IEnumerable<Type> methodParamTypes = method.GetParameters().Select(x => x.ParameterType);
|
||||
if (methodParamTypes.SequenceEqual(argTypes))
|
||||
{
|
||||
return method;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Polymorphic matching
|
||||
foreach (MethodInfo method in methods)
|
||||
{
|
||||
if (method.IsStatic == isStatic)
|
||||
{
|
||||
ParameterInfo[] methodParams = method.GetParameters();
|
||||
if (methodParams.Length == argTypes.Length)
|
||||
{
|
||||
bool isMatch = methodParams
|
||||
.Select(x => x.ParameterType)
|
||||
.Zip(argTypes, (x, y) => (x, y))
|
||||
.All(pair => pair.x.IsAssignableFrom(pair.y));
|
||||
|
||||
if (isMatch)
|
||||
{
|
||||
return method;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new ArgumentException("No overload with the supplied argument types could be found.");
|
||||
}
|
||||
|
||||
private static object[] CreateArgs(MethodInfo method, string[] rawArgs)
|
||||
{
|
||||
ParameterInfo[] methodParams = method.GetParameters();
|
||||
Type[] argTypes = methodParams.Select(x => x.ParameterType).ToArray();
|
||||
return CreateArgs(method, argTypes, rawArgs);
|
||||
}
|
||||
|
||||
private static object[] CreateArgs(MethodInfo method, Type[] argTypes, string[] rawArgs)
|
||||
{
|
||||
ParameterInfo[] methodParams = method.GetParameters();
|
||||
int defaultArgs = methodParams.Count(x => x.HasDefaultValue);
|
||||
|
||||
if (rawArgs.Length < argTypes.Length - defaultArgs || rawArgs.Length > argTypes.Length)
|
||||
{
|
||||
throw new ArgumentException($"Incorrect number ({rawArgs.Length}) of arguments supplied for {Serializer.SerializeFormatted(method.DeclaringType)}.{method.Name}" +
|
||||
$", expected {argTypes.Length}");
|
||||
}
|
||||
|
||||
object[] parsedArgs = new object[argTypes.Length];
|
||||
for (int i = 0; i < parsedArgs.Length; i++)
|
||||
{
|
||||
if (i < rawArgs.Length)
|
||||
{
|
||||
parsedArgs[i] = Parser.Parse(rawArgs[i], argTypes[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
parsedArgs[i] = methodParams[i].DefaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
return parsedArgs;
|
||||
}
|
||||
|
||||
private static object InvokeAndUnwrapException(this MethodInfo method, object[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
return method.Invoke(null, args);
|
||||
}
|
||||
catch (TargetInvocationException e)
|
||||
{
|
||||
throw e.InnerException;
|
||||
}
|
||||
}
|
||||
|
||||
private static object InvokeAndUnwrapException(this MethodInfo method, IEnumerable<object> targets, object[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
return InvocationTargetFactory.InvokeOnTargets(method, targets, args);
|
||||
}
|
||||
catch (TargetInvocationException e)
|
||||
{
|
||||
throw e.InnerException;
|
||||
}
|
||||
}
|
||||
|
||||
[Command("call-static")]
|
||||
private static object CallStatic(Type classType, string funcName)
|
||||
{
|
||||
return CallStatic(classType, funcName, Array.Empty<string>());
|
||||
}
|
||||
|
||||
[Command("call-static")]
|
||||
private static object CallStatic(Type classType, string funcName, string[] args)
|
||||
{
|
||||
MethodInfo[] methods = ExtractMethods(classType, funcName);
|
||||
MethodInfo method = GetIdealOverload(methods, true, args.Length);
|
||||
|
||||
object[] parsedArgs = CreateArgs(method, args);
|
||||
return method.InvokeAndUnwrapException(parsedArgs);
|
||||
}
|
||||
|
||||
[Command("call-static")]
|
||||
[CommandDescription("Invokes the specified static method or property with the provided arguments. Provide [argTypes] if there are ambiguous overloads")]
|
||||
private static object CallStatic(
|
||||
[CommandParameterDescription("Namespace qualified typename of the class.")] Type classType,
|
||||
[CommandParameterDescription("Name of the method or property.")] string funcName,
|
||||
[CommandParameterDescription("The arguments for the function call.")] string[] args,
|
||||
[CommandParameterDescription("The types of the arguments to resolve ambiguous overloads.")] Type[] argTypes)
|
||||
{
|
||||
MethodInfo[] methods = ExtractMethods(classType, funcName);
|
||||
MethodInfo method = GetIdealOverload(methods, true, argTypes);
|
||||
|
||||
object[] parsedArgs = CreateArgs(method, argTypes, args);
|
||||
return method.InvokeAndUnwrapException(parsedArgs);
|
||||
}
|
||||
|
||||
[Command("call-instance")]
|
||||
private static object CallInstance(Type classType, string funcName, MonoTargetType targetType)
|
||||
{
|
||||
return CallInstance(classType, funcName, targetType, Array.Empty<string>());
|
||||
}
|
||||
|
||||
[Command("call-instance")]
|
||||
private static object CallInstance(Type classType, string funcName, MonoTargetType targetType, string[] args)
|
||||
{
|
||||
MethodInfo[] methods = ExtractMethods(classType, funcName);
|
||||
MethodInfo method = GetIdealOverload(methods, false, args.Length);
|
||||
|
||||
object[] parsedArgs = CreateArgs(method, args);
|
||||
IEnumerable<object> targets = InvocationTargetFactory.FindTargets(classType, targetType);
|
||||
return method.InvokeAndUnwrapException(targets, parsedArgs);
|
||||
}
|
||||
|
||||
[Command("call-instance")]
|
||||
[CommandDescription("Invokes the specified non-static method or property with the provided arguments. Provide [argTypes] if there are ambiguous overloads")]
|
||||
private static object CallInstance(
|
||||
[CommandParameterDescription("Namespace qualified typename of the class.")] Type classType,
|
||||
[CommandParameterDescription("Name of the method or property.")] string funcName,
|
||||
[CommandParameterDescription("The MonoTargetType used to find the target instances.")] MonoTargetType targetType,
|
||||
[CommandParameterDescription("The arguments for the function call.")] string[] args,
|
||||
[CommandParameterDescription("The types of the arguments to resolve ambiguous overloads.")] Type[] argTypes)
|
||||
{
|
||||
MethodInfo[] methods = ExtractMethods(classType, funcName);
|
||||
MethodInfo method = GetIdealOverload(methods, false, argTypes);
|
||||
|
||||
object[] parsedArgs = CreateArgs(method, argTypes, args);
|
||||
IEnumerable<object> targets = InvocationTargetFactory.FindTargets(classType, targetType);
|
||||
return method.InvokeAndUnwrapException(targets, parsedArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0ed09ce9eedb9c04fb9915469f53d71a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "QFSW.QC.Extras",
|
||||
"references": [
|
||||
"QFSW.QC"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": []
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dbe7cf6698bd54a63bc00412d71f8b48
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,95 @@
|
||||
#if !QC_DISABLED && !QC_DISABLE_BUILTIN_ALL && !QC_DISABLE_BUILTIN_EXTRA
|
||||
using QFSW.QC.Suggestors.Tags;
|
||||
using QFSW.QC.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace QFSW.QC.Extras
|
||||
{
|
||||
public static class SceneCommands
|
||||
{
|
||||
private static async Task PollUntilAsync(int pollInterval, Func<bool> predicate)
|
||||
{
|
||||
while (!predicate())
|
||||
{
|
||||
await Task.Delay(pollInterval);
|
||||
}
|
||||
}
|
||||
|
||||
[Command("load-scene", "loads a scene by name into the game")]
|
||||
private static async Task LoadScene(
|
||||
[SceneName]
|
||||
string sceneName,
|
||||
|
||||
[CommandParameterDescription("'Single' mode replaces the current scene with the new scene, whereas 'Additive' merges them")]
|
||||
LoadSceneMode loadMode = LoadSceneMode.Single)
|
||||
{
|
||||
AsyncOperation asyncOperation = SceneUtilities.LoadSceneAsync(sceneName, loadMode);
|
||||
await PollUntilAsync(16, () => asyncOperation.isDone);
|
||||
}
|
||||
|
||||
[Command("load-scene-index", "loads a scene by index into the game")]
|
||||
private static async Task LoadScene(int sceneIndex,
|
||||
[CommandParameterDescription("'Single' mode replaces the current scene with the new scene, whereas 'Additive' merges them")]LoadSceneMode loadMode = LoadSceneMode.Single)
|
||||
{
|
||||
AsyncOperation asyncOperation = SceneManager.LoadSceneAsync(sceneIndex, loadMode);
|
||||
await PollUntilAsync(16, () => asyncOperation.isDone);
|
||||
}
|
||||
|
||||
[Command("unload-scene", "unloads a scene by name")]
|
||||
private static async Task UnloadScene([SceneName(LoadedOnly = true)] string sceneName)
|
||||
{
|
||||
AsyncOperation asyncOperation = SceneManager.UnloadSceneAsync(sceneName);
|
||||
await PollUntilAsync(16, () => asyncOperation.isDone);
|
||||
}
|
||||
|
||||
[Command("unload-scene-index", "unloads a scene by index")]
|
||||
private static async Task UnloadScene(int sceneIndex)
|
||||
{
|
||||
AsyncOperation asyncOperation = SceneManager.UnloadSceneAsync(sceneIndex);
|
||||
await PollUntilAsync(16, () => asyncOperation.isDone);
|
||||
}
|
||||
|
||||
[Command("all-scenes", "gets the name and index of every scene included in the build")]
|
||||
private static IEnumerable<KeyValuePair<int, string>> GetAllScenes()
|
||||
{
|
||||
int sceneIndex = 0;
|
||||
foreach (string sceneName in SceneUtilities.GetAllSceneNames())
|
||||
{
|
||||
yield return new KeyValuePair<int, string>(sceneIndex++, sceneName);
|
||||
}
|
||||
}
|
||||
|
||||
[Command("loaded-scenes", "gets the name and index of every scene currently loaded")]
|
||||
private static IEnumerable<KeyValuePair<int, string>> GetLoadedScenes()
|
||||
{
|
||||
return SceneUtilities.GetLoadedScenes()
|
||||
.OrderBy(x => x.buildIndex)
|
||||
.Select(x => new KeyValuePair<int, string>(x.buildIndex, x.name));
|
||||
}
|
||||
|
||||
[Command("active-scene", "gets the name of the active primary scene")]
|
||||
private static string GetCurrentScene()
|
||||
{
|
||||
Scene scene = SceneManager.GetActiveScene();
|
||||
return scene.name;
|
||||
}
|
||||
|
||||
[Command("set-active-scene", "sets the active scene to the scene with name 'sceneName'")]
|
||||
private static void SetActiveScene([SceneName(LoadedOnly = true)] string sceneName)
|
||||
{
|
||||
Scene scene = SceneManager.GetSceneByName(sceneName);
|
||||
if (!scene.isLoaded)
|
||||
{
|
||||
throw new ArgumentException($"Scene {sceneName} must be loaded before it can be set active");
|
||||
}
|
||||
|
||||
SceneManager.SetActiveScene(scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc7a6d8b121ac49c8a37e3d393a51518
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,74 @@
|
||||
#if !QC_DISABLED && !QC_DISABLE_BUILTIN_ALL && !QC_DISABLE_BUILTIN_EXTRA
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QFSW.QC.Extras
|
||||
{
|
||||
public static class ScreenCommands
|
||||
{
|
||||
[Command("fullscreen", "fullscreen state of the application.")]
|
||||
private static bool Fullscreen
|
||||
{
|
||||
get => Screen.fullScreen;
|
||||
set => Screen.fullScreen = value;
|
||||
}
|
||||
|
||||
[Command("screen-dpi", "dpi of the current device's screen.")]
|
||||
private static float DPI => Screen.dpi;
|
||||
|
||||
[Command("screen-orientation", "the orientation of the screen.")]
|
||||
[CommandPlatform(Platform.MobilePlatforms)]
|
||||
private static ScreenOrientation Orientation
|
||||
{
|
||||
get => Screen.orientation;
|
||||
set => Screen.orientation = value;
|
||||
}
|
||||
|
||||
[Command("current-resolution", "current resolution of the application or window.")]
|
||||
private static Resolution GetCurrentResolution()
|
||||
{
|
||||
Resolution resolution = new Resolution
|
||||
{
|
||||
width = Screen.width,
|
||||
height = Screen.height,
|
||||
refreshRate = Screen.currentResolution.refreshRate
|
||||
};
|
||||
|
||||
return resolution;
|
||||
}
|
||||
|
||||
[Command("supported-resolutions", "all resolutions supported by this device in fullscreen mode.")]
|
||||
[CommandPlatform(Platform.AllPlatforms ^ Platform.WebGLPlayer)]
|
||||
private static IEnumerable<Resolution> GetSupportedResolutions()
|
||||
{
|
||||
foreach (Resolution resolution in Screen.resolutions)
|
||||
{
|
||||
yield return resolution;
|
||||
}
|
||||
}
|
||||
|
||||
[Command("set-resolution")]
|
||||
private static void SetResolution(int x, int y)
|
||||
{
|
||||
SetResolution(x, y, Screen.fullScreen);
|
||||
}
|
||||
|
||||
[Command("set-resolution", "sets the resolution of the current application, optionally setting the fullscreen state too.")]
|
||||
private static void SetResolution(int x, int y, bool fullscreen)
|
||||
{
|
||||
Screen.SetResolution(x, y, fullscreen);
|
||||
}
|
||||
|
||||
[Command("capture-screenshot")]
|
||||
[CommandDescription("Captures a screenshot and saves it to the supplied file path as a PNG.\n" +
|
||||
"If superSize is supplied the screenshot will be captured at a higher than native resolution.")]
|
||||
private static void CaptureScreenshot(
|
||||
[CommandParameterDescription("The name of the file to save the screenshot in")] string filename,
|
||||
[CommandParameterDescription("Factor by which to increase resolution")] int superSize = 1
|
||||
)
|
||||
{
|
||||
ScreenCapture.CaptureScreenshot(filename, superSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 937752cbc170542b8ae4a31a025b7cf2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,16 @@
|
||||
#if !QC_DISABLED && !QC_DISABLE_BUILTIN_ALL && !QC_DISABLE_BUILTIN_EXTRA
|
||||
using UnityEngine;
|
||||
|
||||
namespace QFSW.QC.Extras
|
||||
{
|
||||
public static class TimeCommands
|
||||
{
|
||||
[Command("time-scale", "the scale at which time is passing by.")]
|
||||
private static float TimeScale
|
||||
{
|
||||
get => Time.timeScale;
|
||||
set => Time.timeScale = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 816d2412b2b594778a0e42b669424332
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,27 @@
|
||||
#if !QC_DISABLED && !QC_DISABLE_BUILTIN_ALL && !QC_DISABLE_BUILTIN_EXTRA
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QFSW.QC.Extras
|
||||
{
|
||||
public static class TypeCommands
|
||||
{
|
||||
[Command("enum-info", "gets all of the numeric values and value names for the specified enum type.")]
|
||||
private static IEnumerable<object> GetEnumInfo(Type enumType)
|
||||
{
|
||||
if (!enumType.IsEnum) { throw new ArgumentException($"Supplied type '{enumType}' must be an enum type"); }
|
||||
|
||||
Type enumInnerType = enumType.GetEnumUnderlyingType();
|
||||
Array vals = enumType.GetEnumValues();
|
||||
|
||||
for (int i = 0; i < vals.Length; i++)
|
||||
{
|
||||
object name = vals.GetValue(i);
|
||||
object val = Convert.ChangeType(name, enumInnerType);
|
||||
KeyValuePair<object, object> pair = new KeyValuePair<object, object>(val, name);
|
||||
yield return pair;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa2f2993809854c998bd8a5842582d5c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,163 @@
|
||||
#if !QC_DISABLED && !QC_DISABLE_BUILTIN_ALL && !QC_DISABLE_BUILTIN_EXTRA
|
||||
using QFSW.QC.Pooling;
|
||||
using QFSW.QC.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace QFSW.QC.Extras
|
||||
{
|
||||
public static class UtilCommands
|
||||
{
|
||||
private static readonly ConcurrentStringBuilderPool _builderPool = new ConcurrentStringBuilderPool();
|
||||
|
||||
[Command("get-object-info", "Finds the specified GameObject and displays its transform and component data")]
|
||||
private static string ExtractObjectInfo(GameObject target)
|
||||
{
|
||||
StringBuilder builder = _builderPool.GetStringBuilder();
|
||||
|
||||
builder.AppendLine($"Extracted info for object '{target.name}'");
|
||||
builder.AppendLine("Transform data:");
|
||||
builder.AppendLine($" - position: {target.transform.position}");
|
||||
builder.AppendLine($" - rotation: {target.transform.localRotation}");
|
||||
builder.AppendLine($" - scale: {target.transform.localScale}");
|
||||
if (target.transform.childCount > 0) { builder.AppendLine($" - child count: {target.transform.childCount}"); }
|
||||
if (target.transform.parent) { builder.AppendLine($" - parent: {target.transform.parent.name}"); }
|
||||
|
||||
Component[] components = target.GetComponents<Component>().OrderBy(x => x.GetType().Name).ToArray();
|
||||
|
||||
if (components.Length > 0)
|
||||
{
|
||||
builder.AppendLine("Component data:");
|
||||
for (int i = 0; i < components.Length; i++)
|
||||
{
|
||||
int componentCount = 1;
|
||||
Type componentType = components[i].GetType();
|
||||
builder.AppendLine($" - {componentType.Name}");
|
||||
while (i + 1 < components.Length && components[i + 1].GetType() == componentType)
|
||||
{
|
||||
componentCount++;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (componentCount > 1) { builder.Append($" ({componentCount})"); }
|
||||
}
|
||||
}
|
||||
|
||||
if (target.transform.childCount > 0)
|
||||
{
|
||||
builder.AppendLine("Children:");
|
||||
|
||||
int childCount = target.transform.childCount;
|
||||
for (int i = 0; i < childCount; i++)
|
||||
{
|
||||
builder.AppendLine($" - {target.transform.GetChild(i).name}");
|
||||
}
|
||||
}
|
||||
|
||||
return _builderPool.ReleaseAndToString(builder);
|
||||
}
|
||||
|
||||
[Command("get-scene-hierarchy", "Renders the GameObject hierarchy of the currently open scenes")]
|
||||
private static string GetSceneHierarchy()
|
||||
{
|
||||
List<GameObject> objects = new List<GameObject>();
|
||||
StringBuilder buffer = _builderPool.GetStringBuilder();
|
||||
|
||||
foreach (Scene scene in SceneUtilities.GetLoadedScenes())
|
||||
{
|
||||
objects.Clear();
|
||||
scene.GetRootGameObjects(objects);
|
||||
|
||||
buffer.AppendLine(scene.name);
|
||||
GetSceneHierarchy(objects.Select(x => x.transform).ToArray(), 0, buffer, new List<bool>());
|
||||
}
|
||||
|
||||
return _builderPool.ReleaseAndToString(buffer);
|
||||
}
|
||||
|
||||
private static IEnumerable<Transform> GetChildren(this Transform transform)
|
||||
{
|
||||
for (int i = 0; i < transform.childCount; i++)
|
||||
{
|
||||
yield return transform.GetChild(i);
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetSceneHierarchy(IList<Transform> roots, int depth, StringBuilder buffer, IList<bool> drawVertical)
|
||||
{
|
||||
const char terminalSymbol = '|';
|
||||
const char verticalSplitSymbol = '|';
|
||||
const char verticalSymbol = '|';
|
||||
const char horizontalSymbol = '-';
|
||||
const int indentation = 3;
|
||||
|
||||
for (int i = 0; i < roots.Count; i++)
|
||||
{
|
||||
Transform root = roots[i];
|
||||
|
||||
for (int j = 0; j < depth; j++)
|
||||
{
|
||||
buffer.Append(drawVertical[j] ? verticalSymbol : ' ');
|
||||
buffer.Append(' ', indentation - 1);
|
||||
}
|
||||
|
||||
bool terminal = i == roots.Count - 1;
|
||||
drawVertical.Add(!terminal);
|
||||
|
||||
buffer.Append(terminal ? terminalSymbol : verticalSplitSymbol);
|
||||
buffer.Append(horizontalSymbol, indentation - 1);
|
||||
buffer.AppendLine(root.name);
|
||||
|
||||
GetSceneHierarchy(root.GetChildren().ToList(), depth + 1, buffer, drawVertical);
|
||||
drawVertical.RemoveAt(drawVertical.Count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
[Command("add-component", "Adds a component of type T to the specified GameObject")]
|
||||
private static void AddComponent<T>(GameObject target) where T : Component { target.AddComponent<T>(); }
|
||||
|
||||
[Command("destroy-component", "Destroys the component of type T on the specified GameObject")]
|
||||
private static void DestroyComponent<T>(T target) where T : Component { GameObject.Destroy(target); }
|
||||
|
||||
[Command("destroy", "Destroys a GameObject")]
|
||||
private static void DestroyGO(GameObject target) { GameObject.Destroy(target); }
|
||||
|
||||
[Command("instantiate", "Instantiates a GameObject")]
|
||||
private static void InstantiateGO(
|
||||
[CommandParameterDescription("The original GameObject to instantiate a copy of.")] GameObject original,
|
||||
[CommandParameterDescription("The position of the instantiated GameObject.")] Vector3 position,
|
||||
[CommandParameterDescription("The rotation of the instantiated GameObject.")] Quaternion rotation)
|
||||
{
|
||||
GameObject.Instantiate(original, position, rotation);
|
||||
}
|
||||
|
||||
[Command("instantiate", "Instantiates a GameObject")]
|
||||
private static void InstantiateGO(GameObject original, Vector3 position) { GameObject.Instantiate(original).transform.position = position; }
|
||||
|
||||
[Command("instantiate", "Instantiates a GameObject")]
|
||||
private static void InstantiateGO(GameObject original) { GameObject.Instantiate(original); }
|
||||
|
||||
[Command("teleport", "Teleports a GameObject")]
|
||||
private static void TeleportGO(GameObject target, Vector3 position) { target.transform.position = position; }
|
||||
|
||||
[Command("teleport-relative", "Teleports a GameObject by a relative offset to its current position")]
|
||||
private static void TeleportRelativeGO(GameObject target, Vector3 offset) { target.transform.Translate(offset); }
|
||||
|
||||
[Command("rotate", "Rotates a GameObject")]
|
||||
private static void RotateGO(GameObject target, Quaternion rotation) { target.transform.Rotate(rotation.eulerAngles); }
|
||||
|
||||
[Command("set-active", "Activates/deactivates a GameObject")]
|
||||
private static void SetGOActive(GameObject target, bool active) { target.SetActive(active); }
|
||||
|
||||
[Command("set-parent", "Sets the parent of the targert transform.")]
|
||||
private static void SetGOParent(Transform target, Transform parentTarget) { target.SetParent(parentTarget); }
|
||||
|
||||
[Command("send-message", "Calls the method named 'methodName' on every MonoBehaviour in the target GameObject")]
|
||||
private static void SendGOMessage(GameObject target, string methodName) { target.SendMessage(methodName); }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 75e1e5918b90b4de5907e9e5bdf0d9b4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 496cffde696095c439db4ee329062978
|
||||
folderAsset: yes
|
||||
timeCreated: 1553955817
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 015e5afa17239334c8f085479673d346
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
#if NET_4_6 && !NET_STANDARD_2_0
|
||||
#define QC_SUPPORTED
|
||||
#endif
|
||||
|
||||
using Mono.CSharp;
|
||||
using System;
|
||||
using System.CodeDom;
|
||||
using System.CodeDom.Compiler;
|
||||
using System.IO;
|
||||
using System.Reflection.Emit;
|
||||
using System.Text;
|
||||
|
||||
#if QC_SUPPORTED
|
||||
namespace CSharpCompiler
|
||||
{
|
||||
public class CodeCompiler : ICodeCompiler
|
||||
{
|
||||
static long assemblyCounter = 0;
|
||||
|
||||
public CompilerResults CompileAssemblyFromDom(CompilerParameters options, CodeCompileUnit compilationUnit)
|
||||
{
|
||||
return CompileAssemblyFromDomBatch(options, new[] { compilationUnit });
|
||||
}
|
||||
|
||||
public CompilerResults CompileAssemblyFromDomBatch(CompilerParameters options, CodeCompileUnit[] ea)
|
||||
{
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException("options");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return CompileFromDomBatch(options, ea);
|
||||
}
|
||||
finally
|
||||
{
|
||||
options.TempFiles.Delete();
|
||||
}
|
||||
}
|
||||
|
||||
private CompilerResults CompileFromDomBatch(CompilerParameters options, CodeCompileUnit[] ea)
|
||||
{
|
||||
throw new NotImplementedException("sorry ICodeGenerator is not implemented, feel free to fix it and request merge");
|
||||
}
|
||||
|
||||
public CompilerResults CompileAssemblyFromFile(CompilerParameters options, string fileName)
|
||||
{
|
||||
return CompileAssemblyFromFileBatch(options, new[] { fileName });
|
||||
}
|
||||
|
||||
public CompilerResults CompileAssemblyFromFileBatch(CompilerParameters options, string[] fileNames)
|
||||
{
|
||||
var settings = ParamsToSettings(options);
|
||||
|
||||
foreach (var fileName in fileNames)
|
||||
{
|
||||
string path = Path.GetFullPath(fileName);
|
||||
var unit = new SourceFile(fileName, path, settings.SourceFiles.Count + 1);
|
||||
settings.SourceFiles.Add(unit);
|
||||
}
|
||||
|
||||
return CompileFromCompilerSettings(settings, options.GenerateInMemory);
|
||||
}
|
||||
|
||||
public CompilerResults CompileAssemblyFromSource(CompilerParameters options, string source)
|
||||
{
|
||||
return CompileAssemblyFromSourceBatch(options, new[] { source });
|
||||
}
|
||||
|
||||
public CompilerResults CompileAssemblyFromSourceBatch(CompilerParameters options, string[] sources)
|
||||
{
|
||||
var settings = ParamsToSettings(options);
|
||||
|
||||
int i = 0;
|
||||
foreach (var _source in sources)
|
||||
{
|
||||
var source = _source;
|
||||
Func<Stream> getStream = () => { return new MemoryStream(Encoding.UTF8.GetBytes(source ?? "")); };
|
||||
var fileName = i.ToString();
|
||||
var unit = new SourceFile(fileName, fileName, settings.SourceFiles.Count + 1, getStream);
|
||||
settings.SourceFiles.Add(unit);
|
||||
i++;
|
||||
}
|
||||
|
||||
return CompileFromCompilerSettings(settings, options.GenerateInMemory);
|
||||
}
|
||||
|
||||
|
||||
CompilerResults CompileFromCompilerSettings(CompilerSettings settings, bool generateInMemory)
|
||||
{
|
||||
var compilerResults = new CompilerResults(new TempFileCollection(Path.GetTempPath()));
|
||||
var driver = new CustomDynamicDriver(new CompilerContext(settings, new CustomReportPrinter(compilerResults)));
|
||||
|
||||
AssemblyBuilder outAssembly = null;
|
||||
try
|
||||
{
|
||||
driver.Compile(out outAssembly, AppDomain.CurrentDomain, generateInMemory);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
compilerResults.Errors.Add(new CompilerError()
|
||||
{
|
||||
IsWarning = false,
|
||||
ErrorText = e.Message,
|
||||
});
|
||||
}
|
||||
compilerResults.CompiledAssembly = outAssembly;
|
||||
|
||||
return compilerResults;
|
||||
}
|
||||
|
||||
|
||||
CompilerSettings ParamsToSettings(CompilerParameters parameters)
|
||||
{
|
||||
var settings = new CompilerSettings();
|
||||
|
||||
|
||||
foreach (var assembly in parameters.ReferencedAssemblies) settings.AssemblyReferences.Add(assembly);
|
||||
|
||||
settings.Encoding = System.Text.Encoding.UTF8;
|
||||
settings.GenerateDebugInfo = parameters.IncludeDebugInformation;
|
||||
settings.MainClass = parameters.MainClass;
|
||||
settings.Platform = Platform.AnyCPU;
|
||||
settings.StdLibRuntimeVersion = RuntimeVersion.v4;
|
||||
if (parameters.GenerateExecutable)
|
||||
{
|
||||
settings.Target = Target.Exe;
|
||||
settings.TargetExt = ".exe";
|
||||
}
|
||||
else
|
||||
{
|
||||
settings.Target = Target.Library;
|
||||
settings.TargetExt = ".dll";
|
||||
}
|
||||
if (parameters.GenerateInMemory) settings.Target = Target.Library;
|
||||
|
||||
if (string.IsNullOrEmpty(parameters.OutputAssembly))
|
||||
{
|
||||
parameters.OutputAssembly = settings.OutputFile = "DynamicAssembly_" + assemblyCounter + settings.TargetExt;
|
||||
assemblyCounter++;
|
||||
}
|
||||
settings.OutputFile = parameters.OutputAssembly; // if it is not being outputted, we use this to set name of the dynamic assembly
|
||||
|
||||
settings.Version = LanguageVersion.Default;
|
||||
settings.WarningLevel = parameters.WarningLevel;
|
||||
settings.WarningsAreErrors = parameters.TreatWarningsAsErrors;
|
||||
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f3f2a9227250eeb43a8e3c138ce1a6a1
|
||||
timeCreated: 1438909233
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+317
@@ -0,0 +1,317 @@
|
||||
// modified version of Mono.CSharp.Driver
|
||||
|
||||
// driver.cs: The compiler command line driver.
|
||||
//
|
||||
// Authors:
|
||||
// Miguel de Icaza (miguel@gnu.org)
|
||||
// Marek Safar (marek.safar@gmail.com)
|
||||
//
|
||||
// Dual licensed under the terms of the MIT X11 or GNU GPL
|
||||
//
|
||||
// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
|
||||
// Copyright 2004, 2005, 2006, 2007, 2008 Novell, Inc
|
||||
// Copyright 2011 Xamarin Inc
|
||||
//
|
||||
|
||||
#if NET_4_6 && !NET_STANDARD_2_0
|
||||
#define QC_SUPPORTED
|
||||
#endif
|
||||
|
||||
#if QC_SUPPORTED
|
||||
using Mono.CSharp;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace CSharpCompiler
|
||||
{
|
||||
/// <summary>
|
||||
/// The compiler driver.
|
||||
/// </summary>
|
||||
public class CustomDynamicDriver
|
||||
{
|
||||
readonly CompilerContext ctx;
|
||||
|
||||
public CustomDynamicDriver(CompilerContext ctx)
|
||||
{
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
public Report Report
|
||||
{
|
||||
get
|
||||
{
|
||||
return ctx.Report;
|
||||
}
|
||||
}
|
||||
|
||||
void tokenize_file(SourceFile sourceFile, ModuleContainer module, ParserSession session)
|
||||
{
|
||||
Stream input;
|
||||
|
||||
try
|
||||
{
|
||||
input = sourceFile.GetDataStream();
|
||||
}
|
||||
catch
|
||||
{
|
||||
Report.Error(2001, "Source file `" + sourceFile.Name + "' could not be found");
|
||||
return;
|
||||
}
|
||||
|
||||
using (input)
|
||||
{
|
||||
SeekableStreamReader reader = new SeekableStreamReader(input, ctx.Settings.Encoding);
|
||||
var file = new CompilationSourceFile(module, sourceFile);
|
||||
|
||||
Tokenizer lexer = new Tokenizer(reader, file, session, ctx.Report);
|
||||
int token, tokens = 0, errors = 0;
|
||||
|
||||
while ((token = lexer.token()) != Token.EOF)
|
||||
{
|
||||
tokens++;
|
||||
if (token == Token.ERROR)
|
||||
errors++;
|
||||
}
|
||||
Console.WriteLine("Tokenized: " + tokens + " found " + errors + " errors");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
public void Parse(ModuleContainer module)
|
||||
{
|
||||
bool tokenize_only = module.Compiler.Settings.TokenizeOnly;
|
||||
var sources = module.Compiler.SourceFiles;
|
||||
|
||||
Location.Initialize(sources);
|
||||
|
||||
var session = new ParserSession
|
||||
{
|
||||
UseJayGlobalArrays = true,
|
||||
LocatedTokens = new LocatedToken[15000]
|
||||
};
|
||||
|
||||
for (int i = 0; i < sources.Count; ++i)
|
||||
{
|
||||
if (tokenize_only)
|
||||
{
|
||||
tokenize_file(sources[i], module, session);
|
||||
}
|
||||
else
|
||||
{
|
||||
Parse(sources[i], module, session, Report);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Parse(SourceFile file, ModuleContainer module, ParserSession session, Report report)
|
||||
{
|
||||
Stream input;
|
||||
|
||||
try
|
||||
{
|
||||
input = file.GetDataStream();
|
||||
}
|
||||
catch
|
||||
{
|
||||
report.Error(2001, "Source file `{0}' could not be found", file.Name);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check 'MZ' header
|
||||
if (input.ReadByte() == 77 && input.ReadByte() == 90)
|
||||
{
|
||||
|
||||
report.Error(2015, "Source file `{0}' is a binary file and not a text file", file.Name);
|
||||
input.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
input.Position = 0;
|
||||
SeekableStreamReader reader = new SeekableStreamReader(input, ctx.Settings.Encoding, session.StreamReaderBuffer);
|
||||
|
||||
Parse(reader, file, module, session, report);
|
||||
|
||||
if (ctx.Settings.GenerateDebugInfo && report.Errors == 0 && !file.HasChecksum)
|
||||
{
|
||||
input.Position = 0;
|
||||
var checksum = session.GetChecksumAlgorithm();
|
||||
file.SetChecksum(checksum.ComputeHash(input));
|
||||
}
|
||||
|
||||
reader.Dispose();
|
||||
input.Close();
|
||||
}
|
||||
|
||||
public static void Parse(SeekableStreamReader reader, SourceFile sourceFile, ModuleContainer module, ParserSession session, Report report)
|
||||
{
|
||||
var file = new CompilationSourceFile(module, sourceFile);
|
||||
module.AddTypeContainer(file);
|
||||
|
||||
CSharpParser parser = new CSharpParser(reader, file, report, session);
|
||||
parser.parse();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Main compilation method
|
||||
//
|
||||
public bool Compile(out AssemblyBuilder outAssembly, AppDomain domain, bool generateInMemory)
|
||||
{
|
||||
var settings = ctx.Settings;
|
||||
|
||||
outAssembly = null;
|
||||
//
|
||||
// If we are an exe, require a source file for the entry point or
|
||||
// if there is nothing to put in the assembly, and we are not a library
|
||||
//
|
||||
if (settings.FirstSourceFile == null &&
|
||||
((settings.Target == Target.Exe || settings.Target == Target.WinExe || settings.Target == Target.Module) ||
|
||||
settings.Resources == null))
|
||||
{
|
||||
Report.Error(2008, "No files to compile were specified");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (settings.Platform == Platform.AnyCPU32Preferred && (settings.Target == Target.Library || settings.Target == Target.Module))
|
||||
{
|
||||
Report.Error(4023, "Platform option `anycpu32bitpreferred' is valid only for executables");
|
||||
return false;
|
||||
}
|
||||
|
||||
TimeReporter tr = new TimeReporter(settings.Timestamps);
|
||||
ctx.TimeReporter = tr;
|
||||
tr.StartTotal();
|
||||
|
||||
var module = new ModuleContainer(ctx);
|
||||
RootContext.ToplevelTypes = module;
|
||||
|
||||
tr.Start(TimeReporter.TimerType.ParseTotal);
|
||||
Parse(module);
|
||||
tr.Stop(TimeReporter.TimerType.ParseTotal);
|
||||
|
||||
if (Report.Errors > 0)
|
||||
return false;
|
||||
|
||||
if (settings.TokenizeOnly || settings.ParseOnly)
|
||||
{
|
||||
tr.StopTotal();
|
||||
tr.ShowStats();
|
||||
return true;
|
||||
}
|
||||
|
||||
var output_file = settings.OutputFile;
|
||||
string output_file_name;
|
||||
/* if (output_file == null)
|
||||
{
|
||||
var source_file = settings.FirstSourceFile;
|
||||
|
||||
if (source_file == null)
|
||||
{
|
||||
Report.Error(1562, "If no source files are specified you must specify the output file with -out:");
|
||||
return false;
|
||||
}
|
||||
|
||||
output_file_name = source_file.Name;
|
||||
int pos = output_file_name.LastIndexOf('.');
|
||||
|
||||
if (pos > 0)
|
||||
output_file_name = output_file_name.Substring(0, pos);
|
||||
|
||||
output_file_name += settings.TargetExt;
|
||||
output_file = output_file_name;
|
||||
}
|
||||
else
|
||||
{*/
|
||||
output_file_name = Path.GetFileName(output_file);
|
||||
|
||||
/* if (string.IsNullOrEmpty(Path.GetFileNameWithoutExtension(output_file_name)) ||
|
||||
output_file_name.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0)
|
||||
{
|
||||
Report.Error(2021, "Output file name is not valid");
|
||||
return false;
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
var assembly = new AssemblyDefinitionDynamic(module, output_file_name, output_file);
|
||||
module.SetDeclaringAssembly(assembly);
|
||||
|
||||
var importer = new ReflectionImporter(module, ctx.BuiltinTypes);
|
||||
assembly.Importer = importer;
|
||||
|
||||
var loader = new DynamicLoader(importer, ctx);
|
||||
loader.LoadReferences(module);
|
||||
|
||||
if (!ctx.BuiltinTypes.CheckDefinitions(module))
|
||||
return false;
|
||||
|
||||
if (!assembly.Create(domain, AssemblyBuilderAccess.RunAndSave))
|
||||
return false;
|
||||
|
||||
module.CreateContainer();
|
||||
|
||||
loader.LoadModules(assembly, module.GlobalRootNamespace);
|
||||
|
||||
module.InitializePredefinedTypes();
|
||||
|
||||
if (settings.GetResourceStrings != null)
|
||||
module.LoadGetResourceStrings(settings.GetResourceStrings);
|
||||
|
||||
tr.Start(TimeReporter.TimerType.ModuleDefinitionTotal);
|
||||
module.Define();
|
||||
tr.Stop(TimeReporter.TimerType.ModuleDefinitionTotal);
|
||||
|
||||
if (Report.Errors > 0)
|
||||
return false;
|
||||
|
||||
if (settings.DocumentationFile != null)
|
||||
{
|
||||
var doc = new DocumentationBuilder(module);
|
||||
doc.OutputDocComment(output_file, settings.DocumentationFile);
|
||||
}
|
||||
|
||||
assembly.Resolve();
|
||||
|
||||
if (Report.Errors > 0)
|
||||
return false;
|
||||
|
||||
|
||||
tr.Start(TimeReporter.TimerType.EmitTotal);
|
||||
assembly.Emit();
|
||||
tr.Stop(TimeReporter.TimerType.EmitTotal);
|
||||
|
||||
if (Report.Errors > 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
tr.Start(TimeReporter.TimerType.CloseTypes);
|
||||
module.CloseContainer();
|
||||
tr.Stop(TimeReporter.TimerType.CloseTypes);
|
||||
|
||||
tr.Start(TimeReporter.TimerType.Resouces);
|
||||
if (!settings.WriteMetadataOnly)
|
||||
assembly.EmbedResources();
|
||||
tr.Stop(TimeReporter.TimerType.Resouces);
|
||||
|
||||
if (Report.Errors > 0)
|
||||
return false;
|
||||
|
||||
|
||||
if (!generateInMemory) assembly.Save();
|
||||
outAssembly = assembly.Builder;
|
||||
|
||||
|
||||
tr.StopTotal();
|
||||
tr.ShowStats();
|
||||
|
||||
return Report.Errors == 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e389d8a5b0e25ae44a841060cecca276
|
||||
timeCreated: 1435446720
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
#if NET_4_6 && !NET_STANDARD_2_0
|
||||
#define QC_SUPPORTED
|
||||
#endif
|
||||
|
||||
using Mono.CSharp;
|
||||
using System.CodeDom.Compiler;
|
||||
|
||||
#if QC_SUPPORTED
|
||||
namespace CSharpCompiler
|
||||
{
|
||||
|
||||
public class CustomReportPrinter : ReportPrinter
|
||||
{
|
||||
|
||||
readonly CompilerResults compilerResults;
|
||||
#region Properties
|
||||
|
||||
public new int ErrorsCount { get; protected set; }
|
||||
|
||||
public new int WarningsCount { get; private set; }
|
||||
|
||||
#endregion
|
||||
public CustomReportPrinter(CompilerResults compilerResults)
|
||||
{
|
||||
this.compilerResults = compilerResults;
|
||||
}
|
||||
|
||||
public override void Print(AbstractMessage msg, bool showFullPath)
|
||||
{
|
||||
if (msg.IsWarning)
|
||||
{
|
||||
++WarningsCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
++ErrorsCount;
|
||||
}
|
||||
compilerResults.Errors.Add(new CompilerError()
|
||||
{
|
||||
IsWarning = msg.IsWarning,
|
||||
Column = msg.Location.Column,
|
||||
Line = msg.Location.Row,
|
||||
ErrorNumber = msg.Code.ToString(),
|
||||
ErrorText = msg.Text,
|
||||
FileName = showFullPath ? msg.Location.SourceFile.FullPathName : msg.Location.SourceFile.Name,
|
||||
// msg.RelatedSymbols // extra info
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 39ec208e78505e342a2829289445b934
|
||||
timeCreated: 1438909233
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
Implementation of ISynchronizeInvoke for Unity3D game engine.
|
||||
Can be used to invoke anything on main Unity thread.
|
||||
ISynchronizeInvoke is used extensively in .NET forms it's is elegant and quite useful in Unity as well.
|
||||
I implemented it so i can use it with System.IO.FileSystemWatcher.SynchronizingObject.
|
||||
|
||||
help from: http://www.codeproject.com/Articles/12082/A-DelegateQueue-Class
|
||||
example usage: https://gist.github.com/aeroson/90bf21be3fdc4829e631
|
||||
|
||||
license: WTFPL (http://www.wtfpl.net/)
|
||||
contact: aeroson (theaeroson @gmail.com)
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
|
||||
public class DeferredSynchronizeInvoke : ISynchronizeInvoke
|
||||
{
|
||||
Queue<UnityAsyncResult> fifoToExecute = new Queue<UnityAsyncResult>();
|
||||
Thread mainThread;
|
||||
public bool InvokeRequired { get { return mainThread.ManagedThreadId != Thread.CurrentThread.ManagedThreadId; } }
|
||||
|
||||
public DeferredSynchronizeInvoke()
|
||||
{
|
||||
mainThread = Thread.CurrentThread;
|
||||
}
|
||||
public IAsyncResult BeginInvoke(Delegate method, object[] args)
|
||||
{
|
||||
var asyncResult = new UnityAsyncResult()
|
||||
{
|
||||
method = method,
|
||||
args = args,
|
||||
IsCompleted = false,
|
||||
AsyncWaitHandle = new ManualResetEvent(false),
|
||||
};
|
||||
lock (fifoToExecute)
|
||||
{
|
||||
fifoToExecute.Enqueue(asyncResult);
|
||||
}
|
||||
return asyncResult;
|
||||
}
|
||||
public object EndInvoke(IAsyncResult result)
|
||||
{
|
||||
if (!result.IsCompleted)
|
||||
{
|
||||
result.AsyncWaitHandle.WaitOne();
|
||||
}
|
||||
return result.AsyncState;
|
||||
}
|
||||
public object Invoke(Delegate method, object[] args)
|
||||
{
|
||||
if (InvokeRequired)
|
||||
{
|
||||
var asyncResult = BeginInvoke(method, args);
|
||||
return EndInvoke(asyncResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
return method.DynamicInvoke(args);
|
||||
}
|
||||
}
|
||||
public void ProcessQueue()
|
||||
{
|
||||
if (Thread.CurrentThread != mainThread)
|
||||
{
|
||||
throw new TargetException(
|
||||
this.GetType() + "." + MethodBase.GetCurrentMethod().Name + "() " +
|
||||
"must be called from the same thread it was created on " +
|
||||
"(created on thread id: " + mainThread.ManagedThreadId + ", called from thread id: " + Thread.CurrentThread.ManagedThreadId
|
||||
);
|
||||
}
|
||||
bool loop = true;
|
||||
UnityAsyncResult data = null;
|
||||
while (loop)
|
||||
{
|
||||
lock (fifoToExecute)
|
||||
{
|
||||
loop = fifoToExecute.Count > 0;
|
||||
if (!loop) break;
|
||||
data = fifoToExecute.Dequeue();
|
||||
}
|
||||
|
||||
data.AsyncState = Invoke(data.method, data.args);
|
||||
data.IsCompleted = true;
|
||||
}
|
||||
}
|
||||
class UnityAsyncResult : IAsyncResult
|
||||
{
|
||||
public Delegate method;
|
||||
public object[] args;
|
||||
public bool IsCompleted { get; set; }
|
||||
public WaitHandle AsyncWaitHandle { get; internal set; }
|
||||
public object AsyncState { get; set; }
|
||||
public bool CompletedSynchronously { get { return IsCompleted; } }
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f6228134abfaad746965d9639b73908a
|
||||
timeCreated: 1435576341
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,21 @@
|
||||
Copyright (c) 2001, 2002, 2003 Ximian, Inc and the individuals listed
|
||||
on the ChangeLog entries.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 09298e73731a3b64fbedf483cf7d4e42
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2ce9bfedd125d1c49adb5c65c9e8ce53
|
||||
folderAsset: yes
|
||||
timeCreated: 1435432572
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
+30
@@ -0,0 +1,30 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0447405aaa6f3bd4c96d4c64631542fd
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
#if NET_4_6 && !NET_STANDARD_2_0
|
||||
#define QC_SUPPORTED
|
||||
#endif
|
||||
|
||||
using System;
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
#if QC_SUPPORTED
|
||||
namespace CSharpCompiler
|
||||
{
|
||||
public class ScriptBundleLoader
|
||||
{
|
||||
public Func<Type, object> createInstance = (Type type) => { return Activator.CreateInstance(type); };
|
||||
public Action<object> destroyInstance = delegate { };
|
||||
|
||||
public TextWriter logWriter = Console.Out;
|
||||
|
||||
ISynchronizeInvoke synchronizedInvoke;
|
||||
List<ScriptBundle> allFilesBundle = new List<ScriptBundle>();
|
||||
|
||||
public ScriptBundleLoader(ISynchronizeInvoke synchronizedInvoke)
|
||||
{
|
||||
this.synchronizedInvoke = synchronizedInvoke;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="fileSources"></param>
|
||||
/// <returns>true on success, false on failure</returns>
|
||||
public ScriptBundle LoadAndWatchScriptsBundle(IEnumerable<string> fileSources)
|
||||
{
|
||||
var bundle = new ScriptBundle(this, fileSources);
|
||||
allFilesBundle.Add(bundle);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manages a bundle of files which form one assembly, if one file changes entire assembly is recompiled.
|
||||
/// </summary>
|
||||
public class ScriptBundle
|
||||
{
|
||||
Assembly assembly;
|
||||
IEnumerable<string> filePaths;
|
||||
List<FileSystemWatcher> fileSystemWatchers = new List<FileSystemWatcher>();
|
||||
List<object> instances = new List<object>();
|
||||
ScriptBundleLoader manager;
|
||||
|
||||
string[] assemblyReferences;
|
||||
public ScriptBundle(ScriptBundleLoader manager, IEnumerable<string> filePaths)
|
||||
{
|
||||
this.filePaths = filePaths.Select(x => Path.GetFullPath(x));
|
||||
this.manager = manager;
|
||||
|
||||
var domain = System.AppDomain.CurrentDomain;
|
||||
this.assemblyReferences = domain
|
||||
.GetAssemblies()
|
||||
.Where(a => !(a is System.Reflection.Emit.AssemblyBuilder) && !string.IsNullOrEmpty(a.Location))
|
||||
.Select(a => a.Location)
|
||||
.ToArray();
|
||||
|
||||
manager.logWriter.WriteLine("loading " + string.Join(", ", filePaths.ToArray()));
|
||||
CompileFiles();
|
||||
CreateFileWatchers();
|
||||
CreateNewInstances();
|
||||
}
|
||||
|
||||
void CompileFiles()
|
||||
{
|
||||
filePaths = filePaths.Where(x => File.Exists(x)).ToArray();
|
||||
|
||||
var options = new CompilerParameters();
|
||||
options.GenerateExecutable = false;
|
||||
options.GenerateInMemory = true;
|
||||
options.ReferencedAssemblies.AddRange(assemblyReferences);
|
||||
|
||||
var compiler = new CodeCompiler();
|
||||
var result = compiler.CompileAssemblyFromFileBatch(options, filePaths.ToArray());
|
||||
|
||||
foreach (var err in result.Errors)
|
||||
{
|
||||
manager.logWriter.WriteLine(err);
|
||||
}
|
||||
|
||||
this.assembly = result.CompiledAssembly;
|
||||
}
|
||||
void CreateFileWatchers()
|
||||
{
|
||||
foreach (var filePath in filePaths)
|
||||
{
|
||||
FileSystemWatcher watcher = new FileSystemWatcher();
|
||||
fileSystemWatchers.Add(watcher);
|
||||
watcher.Path = Path.GetDirectoryName(filePath);
|
||||
/* Watch for changes in LastAccess and LastWrite times, and
|
||||
the renaming of files or directories. */
|
||||
watcher.NotifyFilter = NotifyFilters.LastWrite
|
||||
| NotifyFilters.FileName | NotifyFilters.DirectoryName;
|
||||
watcher.Filter = Path.GetFileName(filePath);
|
||||
|
||||
// Add event handlers.
|
||||
watcher.Changed += new FileSystemEventHandler((object o, FileSystemEventArgs a) => { Reload(recreateWatchers: false); });
|
||||
//watcher.Created += new FileSystemEventHandler((object o, FileSystemEventArgs a) => { });
|
||||
watcher.Deleted += new FileSystemEventHandler((object o, FileSystemEventArgs a) => { Reload(recreateWatchers: false); });
|
||||
watcher.Renamed += new RenamedEventHandler((object o, RenamedEventArgs a) =>
|
||||
{
|
||||
filePaths = filePaths.Select(x =>
|
||||
{
|
||||
if (x == a.OldFullPath) return a.FullPath;
|
||||
else return x;
|
||||
});
|
||||
Reload(recreateWatchers: true);
|
||||
});
|
||||
watcher.SynchronizingObject = manager.synchronizedInvoke;
|
||||
// Begin watching.
|
||||
watcher.EnableRaisingEvents = true;
|
||||
}
|
||||
}
|
||||
void StopFileWatchers()
|
||||
{
|
||||
foreach (var w in fileSystemWatchers)
|
||||
{
|
||||
w.EnableRaisingEvents = false;
|
||||
w.Dispose();
|
||||
}
|
||||
fileSystemWatchers.Clear();
|
||||
}
|
||||
void Reload(bool recreateWatchers = false)
|
||||
{
|
||||
manager.logWriter.WriteLine("reloading " + string.Join(", ", filePaths.ToArray()));
|
||||
StopInstances();
|
||||
CompileFiles();
|
||||
CreateNewInstances();
|
||||
if (recreateWatchers)
|
||||
{
|
||||
StopFileWatchers();
|
||||
CreateFileWatchers();
|
||||
}
|
||||
}
|
||||
void CreateNewInstances()
|
||||
{
|
||||
if (assembly == null) return;
|
||||
foreach (var type in assembly.GetTypes())
|
||||
{
|
||||
manager.synchronizedInvoke.Invoke((System.Action)(() =>
|
||||
{
|
||||
instances.Add(manager.createInstance(type));
|
||||
}), null);
|
||||
}
|
||||
}
|
||||
void StopInstances()
|
||||
{
|
||||
foreach (var instance in instances)
|
||||
{
|
||||
manager.synchronizedInvoke.Invoke((System.Action)(() =>
|
||||
{
|
||||
manager.destroyInstance(instance);
|
||||
}), null);
|
||||
}
|
||||
instances.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3dfc88ca39a87dc46bc7fd337be863bb
|
||||
timeCreated: 1440503653
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,110 @@
|
||||
#if NET_4_6 && !NET_STANDARD_2_0
|
||||
#define QC_SUPPORTED
|
||||
#endif
|
||||
|
||||
#if QC_SUPPORTED && !QC_DISABLED && !QC_DISABLE_BUILTIN_ALL && !QC_DISABLE_BUILTIN_EXTRA
|
||||
using System;
|
||||
using System.CodeDom.Compiler;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace QFSW.QC
|
||||
{
|
||||
public static class DynamicCodeCommands
|
||||
{
|
||||
private const Platform execAvailability = Platform.AllPlatforms ^ (Platform.WebGLPlayer | Platform.IPhonePlayer | Platform.XboxOne | Platform.PS4 | Platform.Switch);
|
||||
|
||||
[CommandDescription("Loads the code at the specified file and compiles it to C# which will then be executed. Use with caution as no safety checks will be performed. Not supported in AOT (IL2CPP) builds." +
|
||||
"\n\nBy default, boiler plate code will NOT be inserted around the code you provide. Please see 'exec' for more information about boilerplate insertion")]
|
||||
[Command("exec-extern", execAvailability)]
|
||||
private static async Task ExecuteExternalArbitaryCodeAsync(string filePath, bool insertBoilerplate = false)
|
||||
{
|
||||
if (!File.Exists(filePath)) { throw new ArgumentException($"file at the specified path '{filePath}' did not exist."); }
|
||||
string code = File.ReadAllText(filePath);
|
||||
await ExecuteArbitaryCodeAsync(code.Replace("”", "\"").Replace("“", "\""), insertBoilerplate);
|
||||
}
|
||||
|
||||
[CommandDescription("Compiles the given code to C# which will then be executed. Use with caution as no safety checks will be performed. Not supported in AOT (IL2CPP) builds." +
|
||||
"\n\nBy default, boiler plate code will be inserted around the code you provide. This means various namespaces will be included, and the main class and main function entry point will " +
|
||||
"provided. In this case, the code you provide should be code that would exist within the body of the main function, and thus cannot contain things such as class definition. If you " +
|
||||
"disable boiler plate insertion, you can write whatever code you want, however you must provide a static entry point called Main in a static class called Program")]
|
||||
[Command("exec", execAvailability)]
|
||||
private static async Task ExecuteArbitaryCodeAsync(string code, bool insertBoilerplate = true)
|
||||
{
|
||||
#if !UNITY_EDITOR && ENABLE_IL2CPP
|
||||
await Task.FromException(new Exception("exec is not supported on AOT platforms such as IL2CPP and requires JIT (Mono)."));
|
||||
#else
|
||||
MethodInfo entryPoint = await Task.Run(() =>
|
||||
{
|
||||
string fullCode = string.Empty;
|
||||
if (insertBoilerplate)
|
||||
{
|
||||
string[] includedNamespaces = new string[] { "System", "System.Collections", "System.Collections.Generic",
|
||||
"System.Reflection", "System.Linq", "System.Text", "System.Globalization",
|
||||
"UnityEngine", "UnityEngine.Events", "UnityEngine.EventSystems", "UnityEngine.UI" };
|
||||
|
||||
for (int i = 0; i < includedNamespaces.Length; i++) { fullCode += $"using {includedNamespaces[i]};\n"; }
|
||||
fullCode += @"
|
||||
public class Program
|
||||
{
|
||||
public static void Main()
|
||||
{"
|
||||
+ code +
|
||||
@"}
|
||||
}";
|
||||
}
|
||||
else { fullCode = code; }
|
||||
|
||||
Assembly assembly = CompileCode(fullCode);
|
||||
BindingFlags searchFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
|
||||
Type program = assembly.GetType("Program");
|
||||
if (program == null) { throw new ArgumentException("Code Execution Failure - required static class Program could not be found"); }
|
||||
entryPoint = program.GetMethod("Main", searchFlags);
|
||||
if (entryPoint == null) { throw new ArgumentException("Code Execution Failure - required static entry point Main could not be found"); }
|
||||
return entryPoint;
|
||||
});
|
||||
|
||||
entryPoint.Invoke(null, null);
|
||||
#endif
|
||||
}
|
||||
|
||||
private static Assembly CompileCode(string code)
|
||||
{
|
||||
#if !UNITY_EDITOR && ENABLE_IL2CPP
|
||||
throw new Exception("Code compilation is not supported on AOT platforms such as IL2CPP and requires JIT (Mono).");
|
||||
#else
|
||||
CSharpCompiler.CodeCompiler compiler = new CSharpCompiler.CodeCompiler();
|
||||
CompilerParameters compilerParams = new CompilerParameters();
|
||||
Assembly[] allLoadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
|
||||
compilerParams.GenerateExecutable = false;
|
||||
compilerParams.GenerateInMemory = true;
|
||||
for (int i = 0; i < allLoadedAssemblies.Length; i++)
|
||||
{
|
||||
if (!allLoadedAssemblies[i].IsDynamic)
|
||||
{
|
||||
string dllName = allLoadedAssemblies[i].Location;
|
||||
compilerParams.ReferencedAssemblies.Add(dllName);
|
||||
}
|
||||
}
|
||||
|
||||
CompilerResults compiledCode = compiler.CompileAssemblyFromSource(compilerParams, code);
|
||||
|
||||
if (compiledCode.Errors.HasErrors)
|
||||
{
|
||||
string errorMessage = "Code Compilation Failure";
|
||||
for (int i = 0; i < compiledCode.Errors.Count; i++)
|
||||
{
|
||||
errorMessage += $"\n{compiledCode.Errors[i].ErrorNumber} - {compiledCode.Errors[i].ErrorText}";
|
||||
}
|
||||
|
||||
throw new ArgumentException(errorMessage);
|
||||
}
|
||||
|
||||
return compiledCode.CompiledAssembly;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e32a67fb03a2f6548b653203ff9fedf3
|
||||
timeCreated: 1553955853
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user