Я пишу игру Unity, в которой вы пишете код C # / Unity (MonoBehaviours) для решения проблем.Скомпилирую код пользователя в dll и загружаю его в основной домен приложения.Он работает нормально, но проблема в том, что добавленные библиотеки остаются в игре до тех пор, пока она не будет закрыта.
Я нашел решение загрузить эти сборки в отдельный домен приложения, и мне это удалось.Если я останусь в дочернем домене приложения, я не смогу присоединить эти monoBehaviours к gameObject.Я получаю ошибку: «AddComponent запрашивает« {тип} », который не является типом движка Unity».В противном случае, очевидно, я не смогу отправить данные типа обратно в основной домен приложения, поскольку это приведет к загрузке сборки в основном домене приложения.Это также происходит, когда я пытаюсь создать простой сериализуемый класс и отправить его в основной домен.
Версия Unity является персональной 2019.3.0a6.
Чтобы выполнить удаленную загрузку сборок, я компилирую Loader.dll и помещаю его в папку «Ресурсы» моего проекта, чтобы Unity распознала его.
Это загрузчик и большинство вещей которые я пробовал.Моно, которое я пытаюсь добавить и получить, находится в той же сборке, что и Loader:
public class Loader : MarshalByRefObject
{
public void LoadBytes(byte[] bytes) /// Works
{
var assemb = Assembly.Load(bytes);
var types = assemb.GetTypes();
this.LogTypes(types);
}
public void GetComponentFromAttachedObject() ///ArgumentException: GetComponent requires that the requested component '{type}' derives from MonoBehaviour or Component or is an interface.
{
var main = GameObject.Find("Main");
var scr = main.GetComponent<LoaderAssemblyMonoTest>();
scr.Work();
}
public void CreatePrimitiveAndAttach() /// AddComponent asking for "{type}" which is not a Unity engine type.
{
var prim = GameObject.CreatePrimitive(PrimitiveType.Sphere);
Debug.Log("created primitive");
prim.transform.position = new Vector3(10,10,10);
Debug.Log("modified primitive");
var scr = prim.AddComponent<LoaderAssemblyMonoTest>();
Debug.Log("attached scr to primitive");
}
public byte[] SerializeInstance(byte[] bytes) /// Loads the assembly in main domain
{`enter code here`
var assemb = Assembly.Load(bytes);
var types = assemb.GetTypes();
var instance = (IPlugIn)Activator.CreateInstance(types[0]);
var formatter = new BinaryFormatter();
var stream = new MemoryStream();
formatter.Serialize(stream, instance);
return stream.ToArray();
}
public IPlugIn[] LoadPlugins(byte[] bytes) // Loads the assembly in main domain
{
var result = new List<IPlugIn>();
var assemb = Assembly.Load(bytes);
var types = assemb.GetTypes();
foreach (var type in types)
{
if (typeof(IPlugIn).IsAssignableFrom(type))
{
result.Add((IPlugIn)Activator.CreateInstance(type));
}
}
foreach (var plugin in result)
{
Debug.Log("From Loader Domain");
plugin.DoAThing();
}
Debug.Log("After Do A Thing Loader Domain");
return result.ToArray();
}
public void AttachTest() /// AddComponent asking for "{type}" which is not a Unity engine type.
{
var main = GameObject.Find("Main");
main.AddComponent<LoaderAssemblyMonoTest>();
}
public void AttachTestMadTest1(Type type) /// AddComponent asking for "{type}" which is not a Unity engine type.
{
Debug.Log("Here");
var main = GameObject.Find("Main");
main.AddComponent(type);
}
}
Вот как я настраиваю новый домен:
private static AppDomain SetUpAsMainWithLoaderOptimization(string name, LoaderOptimization optimization)
{
var info = AppDomain.CurrentDomain.SetupInformation;
info.LoaderOptimization = optimization;
var domain = AppDomain.CreateDomain(
name,
AppDomain.CurrentDomain.Evidence,
info);
return domain;
}
Вот как я добавляюсборки к нему:
public static void AddAllCurrentDomainAssembliesToDomain(AppDomain domain)
{
foreach (var ass in AppDomain.CurrentDomain.GetAssemblies())
{
domain.Load(File.ReadAllBytes(ass.Location));
}
}
Так я запускаю загрузчик.Я не загружаю сборку сначала, потому что с предыдущим способом она уже там.
private static Loader InitLoader(AppDomain domain)
{
//domain.Load(typeof(Loader).Assembly.FullName);
Loader loader = (Loader)Activator.CreateInstance(
domain,
typeof(Loader).Assembly.FullName,
typeof(Loader).FullName,
false,
BindingFlags.Public | BindingFlags.Instance,
null,
null,
null,
null).Unwrap();
return loader;
}
Это один из примеров того, как я его использую:
public static void AttachMonoFromSeparateDomain ()
{
userDomain = SetUpAsMainWithLoaderOptimization("testy", LoaderOptimization.MultiDomain);
AddAllCurrentDomainAssembliesToDomain(userDomain);
userDomain.AssemblyResolve += AssemblyResolveSeparateDomain;
AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolveMainDomain;
var main = GameObject.Find("Main");
main.AddComponent<LoaderAssemblyMonoTest>();
var loader = InitLoader(userDomain);
loader.GetComponentFromAttachedObject();
}
Из того, что я прочитал, я ожидал, что отправка сериализуемого класса через домены приложения не должна приводить к загрузке сборки вОсновное приложение, но это так.Будем весьма благодарны за любые идеи о том, как сделать это без утечек памяти или как прикрепить monoBehaviours из дочернего домена appDomain!