Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Feedback DOTS Mod Support with loading and unloading dlls (Resolved but at what cost ;n;)

Discussion in 'Entity Component System' started by NT_Ninetails, Jan 8, 2022.

  1. NT_Ninetails

    NT_Ninetails

    Joined:
    Jan 21, 2018
    Posts:
    191
    So i followed the tutorial for DOTS Mod support and successfully loaded a mod into my test project. So I was like, "How do I unload a mod?" and when doing research I came across AppDomain but it seems that Unity just straight up doesn't support this. My question is, are there any plans to support this or is there a super secret way you can do this without AppDomain or without using C/C++ code?

    This is the Unity tutorial I followed:
    https://docs.unity3d.com/Packages/com.unity.burst@1.6/manual/docs/ModdingSupport.html
     
  2. MrCool92

    MrCool92

    Joined:
    Jul 13, 2015
    Posts:
    26
    I think you would have to restart app and load only mods you want.
     
  3. NT_Ninetails

    NT_Ninetails

    Joined:
    Jan 21, 2018
    Posts:
    191
    For now this is what I have concluded as well, though I wish it was not the case.

    After performing some more tests it seems that when using the AppDomain approach the PrivateBinPath isn't being used so the only way to load the dll with an AppDomain is to have it included in the Managed dll folder. This however is not ideal so i'm still looking into it.
     
  4. NT_Ninetails

    NT_Ninetails

    Joined:
    Jan 21, 2018
    Posts:
    191
    lol i think I solved it....i need to perform more tests real quick
     
  5. NT_Ninetails

    NT_Ninetails

    Joined:
    Jan 21, 2018
    Posts:
    191
    WOW, I did solve it and MAAAAAAAAAN it gives me JavaScript vibes. I'll try my best to document this but I have a lot of tabs open and jumbled code so bear with me.

    Introduction

    So Unity has blessed us with a neat tutorial on how to add Mod support to our games located here:
    https://docs.unity3d.com/Packages/com.unity.burst@1.6/manual/docs/ModdingSupport.html

    some things you may need to know to get this working.
    1) what ever data type/structure (class, interface, etc.) you want to mod to reference MUST come from the initial Program. For example, I had
    Code (CSharp):
    1. public interface SystemInterface_Old
    2. {
    3.     public void Update();
    4.     public void OnCreate();
    5.     public void OnDestroy();
    6.     public void OnStartRunning();
    7.     public void OnStopRunning();
    8. }
    This interface in is a file named SystemInterface.cs which shares a folder with an AssemblyDefinition named ModInterfaces (This is very much recommended and probably required)

    Now I will Build the Main Project and in the Build's Managed folder I see my fancy new Assembly named Modinterfaces.dll

    now I take that dll file and place it in the Plugins folder of my ModVersion Project (yes you need two projects for this). now that my ModVersion project has the dll loaded as a plugin you won't get the error saying "Can't Load MyInterface.dll cause its missing or something and i'm not a very descriptive error blah blah". This error will may popup because of C# dll versioning or signatures or something...I'm sorry I lost the post and cannot explain further. So in short, always use Build versions of your dlls between projects.

    so anyway let's say your Mod is like this:
    Code (CSharp):
    1. using UnityEngine;
    2. using Unity.Entities;
    3. using Unity.Burst;
    4. using Unity.Jobs;
    5.  
    6. [System.Serializable]
    7. public  struct TestSystem : SystemInterface
    8. {
    9.     public int xxy;
    10.     public void OnCreate()
    11.     {
    12.         Debug.Log("OnCreate is called!");
    13.     }
    14.  
    15.     public  void OnDestroy()
    16.     {
    17.         Debug.Log("OnDestroy is called!");
    18.     }
    19.  
    20.     public void OnStartRunning()
    21.     {
    22.         Debug.Log("OnStartRunning is called!");
    23.     }
    24.  
    25.     public  void OnStopRunning()
    26.     {
    27.         Debug.Log("OnStopRunning is called!");
    28.     }
    29.  
    30.     public override void Update()
    31.     {
    32.         Debug.Log("Update is called!");
    33.         Debug.Log(xxy);
    34.         xxy++;
    35.  
    36.         var burst_job = new BurstJobTest { y = xxy };
    37.         burst_job.Schedule().Complete();
    38.         Debug.Log("job is "+(burst_job.y > 0 ? "Bursted" : "Not Bursted")+" at "+burst_job.y);
    39.  
    40.         A a = new A { };
    41.         Debug.Log("Created A");
    42.  
    43.     }
    44. }
    45.  
    46. struct A { }
    47.  
    48. [BurstCompile]
    49. struct BurstJobTest : IJob
    50. {
    51.     public int y;
    52.  
    53.     [BurstDiscard]
    54.     void MakeNagative()
    55.     {
    56.         y *= -1;
    57.     }
    58.  
    59.     public void Execute()
    60.     {
    61.         y++;
    62.         MakeNagative();
    63.     }
    64. }
    65.  
    cool. if you follow the rest of Unity's turorial and use thier scripts then you should be fine.

    But what if I want to unload the Mod's dll from the main AppDomain?
    - you can't without restarting the program....unless it's in a different AppDomain....

    So now we get into hosting the Modded code in a new AppDomain! After many many many trial and errors I learned that for some reason, in Unity, when you create a new AppDomain it will always load referenced dlls in the "Managed" dll folder (for the Editor this would be in Library/ScriptAssemblies) which is because this is essentially the "bin" folder. so you may be thinking "I followed the tutorial and I loaded the Mod in a Mods folder why does this information even matter?", this is because this is the NEW AppDomain and your Mod is not considered to be in that "bin" folder anymore so you will get an error saying something like "Cannot load TestMod_Managed.dll because I ignore the path you gave me and couldn't find it in my special place". "But wait! you can set the PrivateBinePath in the AppDomainSettings right?", yes but doing the "normal" way doesn't work and the custom bin path gets ignored. "So how can I fix this?", it's complicated but what I did was use these scripts located at this github repo and it solved that problem:
    stackoverflow link (checkout Jduv's answer and comments): https://stackoverflow.com/questions...-to-appdomain-with-all-references-recursively
    https://github.com/jduv/AppDomainToolkit

    you: "So cool, now I have set my "bin" path to the mod folder, have all my mod references there, and loaded the dlls in another AppDomain so I can unload the mod dll right?"

    me: "yes"

    you: "so when I call AppDomainContext.Dispose() the loaded dlls will be removed from the program?"

    me: "no"

    you: "what?"


    Since the code used to load the assembly into the new AppDomain was executed on the main AppDomain, you loaded it on both the main and new AppDomains. pretty cool right?

    reference: https://stackoverflow.com/questions...-assembly-i-loaded-up-with-reflection/2064434

    to fix this you have to create a new AppDomain...let's name them first. We now have MainAppDomain (Your Unity Program), AppDomain1 and AppDomain2. you first create AppDomain1 then execute code within AppDomain1 to create AppDomain2 which loads the Mod and its references.

    you: "ok so now I can execute my interface methods on the MainAppDomain with the loadded dlls in AppDomain 1 & 2 right?"

    me: "interface? that isn't allowed, and nether are abstract classes. only Serialiable stuff"

    you: "what why?"

    me: "because in order to execute code in another specific domain you need to call domain.DoCallBack() which only accepts void methods from classes that are Serializable"

    So now our "interface" code looks like:

    Code (CSharp):
    1. using System;
    2.  
    3. [Serializable]
    4. public class SystemInterface
    5. {
    6.     public virtual void Update() { }
    7.     public virtual void OnCreate() { }
    8.     public virtual void OnDestroy() { }
    9.     public virtual void OnStartRunning(){ }
    10.     public virtual void OnStopRunning(){ }
    11. }
    12.  
    and the mod code looks like:

    Code (CSharp):
    1. using UnityEngine;
    2. using Unity.Entities;
    3. using Unity.Burst;
    4. using Unity.Jobs;
    5.  
    6. [System.Serializable]
    7. public  class TestSystem : SystemInterface
    8. {
    9.     public int xxy;
    10.     public override void OnCreate()
    11.     {
    12.         Debug.Log("OnCreate is called!");
    13.     }
    14.  
    15.     public override void OnDestroy()
    16.     {
    17.         Debug.Log("OnDestroy is called!");
    18.     }
    19.  
    20.     public override void OnStartRunning()
    21.     {
    22.         Debug.Log("OnStartRunning is called!");
    23.     }
    24.  
    25.     public override void OnStopRunning()
    26.     {
    27.         Debug.Log("OnStopRunning is called!");
    28.     }
    29.  
    30.     public override void Update()
    31.     {
    32.         Debug.Log("Update is called!");
    33.         Debug.Log(xxy);
    34.         xxy++;
    35.  
    36.         var burst_job = new BurstJobTest { y = xxy };
    37.         burst_job.Schedule().Complete();
    38.         Debug.Log("job is "+(burst_job.y > 0 ? "Bursted" : "Not Bursted")+" at "+burst_job.y);
    39.  
    40.         A a = new A { };
    41.         Debug.Log("Created A");
    42.  
    43.     }
    44. }
    45.  
    46. struct A { }
    47.  
    48. [BurstCompile]
    49. struct BurstJobTest : IJob
    50. {
    51.     public int y;
    52.  
    53.     [BurstDiscard]
    54.     void MakeNagative()
    55.     {
    56.         y *= -1;
    57.     }
    58.  
    59.     public void Execute()
    60.     {
    61.         y++;
    62.         MakeNagative();
    63.     }
    64. }
    65.  
    SO THERE WE HAVE IT! you have successfully loaded the mod in a seperate AppDomain!

    you: "cool so the mod is in a seperate AppDomain and now I feel safe, so how do I get my data from the other domain?"

    me: "you use domain.GetData() and domain.SetData()"

    you: "so i can use domain.SetData("data",mySystemInterfaceObject) inside the new AppDomain and var domain.GetData("data") in the main AppDomain?"

    me: "yes"

    you: "cool, I just cast that object back into a SystemInterface and all is well!"

    me: "excuse me, did you just cast from a derived class to a base class in the main AppDomain?"

    you: "yes what's the problem?"

    me: "how are you going to cast to SystemInterface WHEN THE DERIVED CLASS DOESN'T EXISTS IN THE MAIN APPDOMAIN!"

    you: "what do you mean? it worked in the Unity tutorial!"

    me: "OF COURSE IT DID, YOU LOADED THE DLL ON THE MAIN APPDOMAIN, THAT'S WHY WE'RE HERE TRYING TO PUT IT IN A NEW APPDOMAIN"

    you: "so how will I call my methods from the main AppDomain where my game runs?"

    me: "after you save the data in the new AppDomain you have to create a function that calls GetData() and use the Type.InvokeMember on in"

    Here is the code I use on the main application. I use a SystemBase to load the mod (it is very rough and unrefined but it works):

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3. using Unity.Entities;
    4. using System.IO;
    5. using System.Reflection;
    6. using System;
    7. using Unity.Burst;
    8. using AppDomainToolkit;
    9.  
    10. [Serializable]
    11. class CallbackContext
    12. {
    13.     public AppDomainSetup setupInfo { get; set; }
    14.     public string dllFile { get; set; }
    15.     public int counter { get; set; }
    16.  
    17.     public SystemInterface ISystem;
    18.     public void Execute()
    19.     {
    20.         using (var context = AppDomainContext.Create(setupInfo))
    21.         {
    22.             try
    23.             {
    24.                 Debug.Log("Loading file \"" + dllFile);
    25.  
    26.                 var _managedPlugin = context.LoadAssemblyWithReferences(LoadMethod.LoadFrom, dllFile);
    27.                 string mod_name = Path.GetFileName(dllFile).Split('.')[0];
    28.  
    29.                 Debug.Log("Current Domain Loaded assemblies: " + AppDomain.CurrentDomain.GetAssemblies().Length);
    30.                 Debug.Log("Other App Domain Loaded assemblies: " + context.Domain.GetAssemblies().Length);
    31.                 foreach (var assembly in context.Domain.GetAssemblies())
    32.                 {
    33.                     Debug.Log("Other App Domain has loaded \"" + assembly.FullName + "\"");
    34.                     if (assembly.FullName.Contains(mod_name))
    35.                     {
    36.                         Debug.Log("Found mod file, getting interface/class that i need");
    37.  
    38.                         var types = assembly.GetTypes();
    39.                         foreach (var type in types)
    40.                         {
    41.                             Debug.Log("Attempting to create SystemInterface for \"" + type.FullName + "\"");
    42.                             try
    43.                             {
    44.                                 var plugin = (SystemInterface)Activator.CreateInstance(type);
    45.                                 if (plugin == null) Debug.LogError("Failed to Create Plugin Instance");
    46.                                 else
    47.                                 {
    48.                                     ISystem = plugin;
    49.                                     Debug.Log("III "+ISystem.GetType().Name+", "+ISystem.GetType().FullName+", "+ISystem.GetType().BaseType.FullName);
    50.                                  
    51.                                     AppDomain.CurrentDomain.SetData("ISystems", ISystem);
    52.                                     Debug.Log("Successfully Loaded Managed Assmbly \"" + dllFile + "\"");
    53.                                     break;
    54.                                 }
    55.                             }
    56.                             catch (Exception e)
    57.                             {
    58.                                 Debug.LogWarning(e.Message + "\n" + e.StackTrace);
    59.                             }
    60.                         }
    61.                     }
    62.                     else Debug.LogWarning("did not found \""+mod_name+"\" in \""+assembly.FullName+"\"");
    63.                 }
    64.             //    context.Dispose();
    65.                 AppDomain.CurrentDomain.SetData("counter",-200);
    66.             }
    67.             catch (Exception e)
    68.             {
    69.                 Debug.LogWarning(e.Message + "\n" + e.StackTrace);
    70.                 counter = -100;
    71.             }
    72.  
    73.         }
    74.     }
    75.  
    76.  
    77.     string memberName;
    78.     BindingFlags bindingFlags;
    79.     Binder binder;
    80.     object target;
    81.     object[] args;
    82.     public void InvokeMember()
    83.     {
    84.         ISystem = (SystemInterface)AppDomain.CurrentDomain.GetData("ISystems");
    85.         if (ISystem == null) Debug.LogWarning("ISSSSS is null here too!");
    86.         ISystem.GetType().InvokeMember(memberName, bindingFlags, binder, target == null ? ISystem : target, args);
    87.     }
    88.     public void SetInvokeParameters(string memberName, BindingFlags bindingFlags, Binder binder, object target, object[] args)
    89.     {
    90.         this.memberName = memberName;
    91.         this.bindingFlags = bindingFlags;
    92.         this.binder = binder;
    93.         this.target = target;
    94.         this.args = args;
    95.     }
    96. }
    97. public class DOTSSystemPluginManager : SystemBase
    98. {
    99.     string MOD_FOLDER = Path.GetFullPath(Path.Combine(Application.dataPath, "..", "Mods"));
    100.     string MANAGED_DLL_NAME_REQUIREMENT = "_Managed.dll";
    101.     string BURST_DLL_NAME_REQUIREMENT = "_win_x86_64.dll";
    102.  
    103.     List<SystemInterface> ModSystems;
    104.     List<Assembly> Assemblies;
    105.     List<AppDomain> AppDomains;
    106.  
    107.     int counter = 0;
    108.  
    109.  
    110.     protected override void OnCreate()
    111.     {
    112.         ModSystems = new List<SystemInterface>();
    113.         Assemblies = new List<Assembly>();
    114.         AppDomains = new List<AppDomain>();
    115.  
    116.         Debug.Log("Currently Loaded Assemblies ON START= " + AppDomain.CurrentDomain.GetAssemblies().Length);
    117.         Debug.Log("AppDomain information: "+
    118.             AppDomain.CurrentDomain.BaseDirectory+"\n"+AppDomain.CurrentDomain.RelativeSearchPath+"\n"+AppDomain.CurrentDomain.FriendlyName
    119.             +AppDomain.CurrentDomain.GetAssemblies()[AppDomain.CurrentDomain.GetAssemblies().Length-1].FullName+"\n");
    120.         Debug.Log("Mod Folder is at "+MOD_FOLDER);
    121.         if (Directory.Exists(MOD_FOLDER))
    122.         {
    123.             var modFolders = Directory.GetDirectories(MOD_FOLDER);
    124.             if (modFolders.Length == 0) Debug.LogWarning("no mod folders detected!");
    125.             foreach (var modFolder in modFolders)
    126.             {
    127.                 var dllFolder = Path.Combine(modFolder,"dll");
    128.                 if (Directory.Exists(dllFolder))
    129.                 {
    130.                     var dllFiles = Directory.GetFiles(dllFolder);
    131.                     if (dllFiles.Length == 0) Debug.LogWarning("dll folder has no dlls!");
    132.                     foreach(var dllFile in dllFiles)
    133.                     {
    134.                         if (dllFile.Contains(MANAGED_DLL_NAME_REQUIREMENT))
    135.                         {
    136.                             Debug.Log("dllFolder = \"" + dllFolder + "\" mod folder path " + " \"" + modFolder + "\"");
    137.                             AppDomainSetup domaininfo = new AppDomainSetup();
    138.                             domaininfo.ApplicationBase = dllFolder;
    139.                             domaininfo.PrivateBinPath = dllFolder;
    140.                             domaininfo.ShadowCopyFiles = "true";
    141.                             domaininfo.ShadowCopyDirectories = Path.Combine(domaininfo.ApplicationBase, "Mods");
    142.                             /*     AppDomain domain = AppDomain.CreateDomain("MyDomain", AppDomain.CurrentDomain.Evidence, domaininfo);
    143.                                  Type ttype = typeof(Proxy);
    144.                                  Debug.Log("so " + domain.BaseDirectory + ", " + ttype.Assembly.FullName + ", " + ttype.FullName);
    145.                                  var value = (Proxy)domain.CreateInstanceAndUnwrap(
    146.                                      ttype.Assembly.FullName,
    147.                                      ttype.FullName);*/
    148.                             var setupInfo = new AppDomainSetup()
    149.                             {
    150.                                 ApplicationName = "My Application",
    151.                                 ApplicationBase = dllFolder,
    152.                                 PrivateBinPath = dllFolder,
    153.                                 ShadowCopyFiles = "true",
    154.                                 ShadowCopyDirectories = "true",
    155.                                 LoaderOptimization = LoaderOptimization.MultiDomainHost
    156.                             };
    157.  
    158.                             IAssemblyTarget managedPlugin = null;
    159.                             using (var contextA = AppDomainContext.Create(setupInfo))
    160.                             {
    161.                                 CallbackContext ctx = new CallbackContext();
    162.                                 ctx.setupInfo = setupInfo;
    163.                                 ctx.dllFile = dllFile;
    164.                                 ctx.counter = counter;
    165.                                 contextA.Domain.DoCallBack(ctx.Execute);
    166.                                 Debug.Log("counter after callback = "+(int)contextA.Domain.GetData("counter"));
    167.                                 counter = (int)contextA.Domain.GetData("counter");
    168.                                 Debug.Log("111 is null?"+(ctx.ISystem == null));
    169.                                 ctx.SetInvokeParameters("Update", BindingFlags.DeclaredOnly |
    170.             BindingFlags.Public | BindingFlags.NonPublic |
    171.             BindingFlags.Instance | BindingFlags.InvokeMethod, null, null, new object[0] { }
    172.                                            );
    173.                                 contextA.Domain.DoCallBack(ctx.InvokeMember);
    174.                               /*  var systems_object = contextA.Domain.GetData("ISystems");
    175.  
    176.                                 if (systems_object == null) Debug.LogWarning("Failed to get ISystem!");
    177.                                 else
    178.                                 {
    179.                                     Debug.Log(systems_object.GetType().FullName);
    180.                                     try
    181.                                     {
    182.                                         Debug.Log("INVOLKING METHOD OF A TYPE THAT DOESN'T EXIST IN MY ASSEMBLY!");
    183.                                         systems_object.GetType().InvokeMember(
    184.                                             "Update", BindingFlags.DeclaredOnly |
    185.             BindingFlags.Public | BindingFlags.NonPublic |
    186.             BindingFlags.Instance | BindingFlags.InvokeMethod, null, systems_object, new object[0] { }
    187.                                             );
    188.                                         Debug.Log("INVOKING DONE!");
    189.                                     }catch(Exception e)
    190.                                     {
    191.                                         Debug.LogError(e.Message+"\n"+e.StackTrace);
    192.                                     }
    193.                                     //SystemInterface i = (SystemInterface)systems_object;
    194.                                     //ModSystems.Add(i);
    195.                                 }*/
    196.                                 contextA.Dispose();
    197.                             }
    198.  
    199.  
    200.                          
    201.                             Debug.Log("Current Domain Loaded assemblies: " + AppDomain.CurrentDomain.GetAssemblies().Length);
    202.                             managedPlugin = null;
    203.                        
    204.                             //Debug.Log("Current Domain Loaded assemblies After Dispose: " + AppDomain.CurrentDomain.GetAssemblies().Length+" is disposed = "+context.IsDisposed);
    205.                          
    206.  
    207.  
    208.                             // AppDomain.Unload(domain);
    209.  
    210.  
    211.                             //   var managedPlugin = Assembly.Load(File.ReadAllBytes(dllFile));
    212.                             if (managedPlugin != null)
    213.                             {
    214.                                 Debug.Log("got plugin!"+managedPlugin.IsDynamic+", "+managedPlugin.FullName+", "+managedPlugin.Location+", "+managedPlugin.CodeBase);
    215.                            
    216.  
    217.                              
    218.                                 /*  var types = managedPlugin.GetTypes();
    219.                                 foreach (var type in types)
    220.                                 {
    221.                                     Debug.Log("Attempting to create SystemInterface for \""+type.FullName+"\"");
    222.                                     try
    223.                                     {
    224.                                         var plugin = (SystemInterface)Activator.CreateInstance(type);
    225.                                         if (plugin == null) Debug.LogError("Failed to Create Plugin Instance");
    226.                                         else
    227.                                         {
    228.                                             ModSystems.Add(plugin);
    229.                                             Debug.Log("Successfully Loaded Managed Assmbly \""+dllFile+"\"");
    230.                                         }
    231.                                     }
    232.                                     catch (Exception e)
    233.                                     {
    234.                                         Debug.LogWarning(e.Message + "\n" + e.StackTrace);
    235.                                     }
    236.                                 }
    237.                                 Assemblies.Add(managedPlugin);
    238.                                 AppDomains.Add(domain);*/
    239.                             }
    240.                             else Debug.LogWarning("managedPlugin is null!");
    241.                         }else if (dllFile.Contains(BURST_DLL_NAME_REQUIREMENT))
    242.                         {
    243.                             try
    244.                             {
    245.                                 BurstRuntime.LoadAdditionalLibrary(dllFile);
    246.                                 Debug.Log("Successfully Loaded Burst Assembly \""+dllFile+"\"");
    247.                             }
    248.                             catch (Exception e)
    249.                             {
    250.                                 Debug.LogWarning(e.Message+"\n"+e.StackTrace);
    251.                             }
    252.                         }
    253.                     }
    254.                 }
    255.                 else Debug.LogWarning("the mod folder doesn't contain a dll folder to be loaded!");
    256.             }
    257.         }
    258.         else Debug.LogWarning("Cannot load at mod path \"" + MOD_FOLDER+"\"");
    259.  
    260.         Debug.Log("Loaded "+ModSystems.Count+" Mod Systems!");
    261.  
    262.         foreach (var system in ModSystems)
    263.         {
    264.             system.OnCreate();
    265.         }
    266.     }
    267.  
    268.     void DeleteAssemblies()
    269.     {
    270.         Debug.Log("Currently Loaded Assemblies = "+AppDomain.CurrentDomain.GetAssemblies().Length);
    271.         while(ModSystems.Count > 0)
    272.         {
    273.             ModSystems[0] = null;
    274.             ModSystems.RemoveAt(0);
    275.         }
    276.         while(AppDomains.Count > 0)
    277.         {
    278.             Debug.Log("Created AppDomain has "+AppDomains[0].GetAssemblies().Length+" assemblies ");
    279.             foreach (var a in AppDomains[0].GetAssemblies())
    280.                 Debug.Log("Created AppDomain has \""+a.FullName+"\"");
    281.             AppDomain.Unload(AppDomains[0]);
    282.             AppDomains[0] = null;
    283.             AppDomains.RemoveAt(0);
    284.         }
    285.      /*   while(Assemblies.Count > 0)
    286.         {
    287.             Assemblies[0] = null;
    288.             Assemblies.RemoveAt(0);
    289.         }*/
    290.         Debug.Log("Loaded Assemblies After Delete = " + AppDomain.CurrentDomain.GetAssemblies().Length);
    291.     }
    292.  
    293.     protected override void OnStartRunning()
    294.     {
    295.         foreach (var system in ModSystems)
    296.         {
    297.             system.OnStartRunning();
    298.         }
    299.     }
    300.     protected override void OnStopRunning()
    301.     {
    302.         foreach (var system in ModSystems)
    303.         {
    304.             system.OnStopRunning();
    305.         };
    306.         DeleteAssemblies();
    307.     }
    308.     protected override void OnDestroy()
    309.     {
    310.         foreach (var system in ModSystems)
    311.         {
    312.             system.OnDestroy();
    313.         }
    314.  
    315.         DeleteAssemblies();
    316.     }
    317.  
    318.     protected override void OnUpdate()
    319.     {
    320.         foreach (var system in ModSystems)
    321.         {
    322.             system.Update();
    323.         }
    324.         if(counter >= 0)
    325.             counter++;
    326.         if (counter > 10)
    327.         {
    328.             OnDestroy();
    329.             counter = -1;
    330.         }
    331.         Debug.Log("counter = "+counter);
    332.     }
    333. }
    334.  



     
    Last edited: Jan 11, 2022
    Homolus, apkdev, exiguous and 3 others like this.
  6. NT_Ninetails

    NT_Ninetails

    Joined:
    Jan 21, 2018
    Posts:
    191
    I generalized part of the code

    Code (CSharp):
    1.  
    2.  
    3.     string memberName, dataName;
    4.     BindingFlags bindingFlags;
    5.     Binder binder;
    6.     object target;
    7.     object[] args;
    8.     public void InvokeMember()
    9.     {
    10.         target = AppDomain.CurrentDomain.GetData(dataName);
    11.         target.GetType().InvokeMember(memberName, bindingFlags, binder,target, args);
    12.     }
    13.     public void SetInvokeParameters(string dataName,string memberName, BindingFlags bindingFlags, Binder binder, object target, object[] args)
    14.     {
    15.         this.memberName = memberName;
    16.         this.bindingFlags = bindingFlags;
    17.         this.binder = binder;
    18.         this.target = target;
    19.         this.args = args;
    20.         this.dataName = dataName;
    21.     }
     
  7. iamarugin

    iamarugin

    Joined:
    Dec 17, 2014
    Posts:
    863
    Well, that's rough
     
    apkdev likes this.
  8. NT_Ninetails

    NT_Ninetails

    Joined:
    Jan 21, 2018
    Posts:
    191
    yep, I also forgot to add if you use domain.GetData() to acquire an object that is of an derived class that is not in the main AppDomain, it will load the dll nesscary to execute its code onto the main AppDomain which is why I opted to execute the code like "Update" in the other domains.

    As far as performance goes I guess it doesn't matter as long as your not constantly trying to getData() and setData() a lot right? I assume that since the loadded mod dll can be coded just like a normal unity program (with burst and all that) you could run the mod without any performance benefits....buts that's just speculation.

    Any thoughts?