Adam's Lair Forum

game development and casual madness
It is currently 2019/09/17, 15:22

All times are UTC + 1 hour [ DST ]




Post new topic Reply to topic  [ 8 posts ] 
Author Message
PostPosted: 2013/12/19, 19:40 
Novice Member
Novice Member

Joined: 2013/05/21, 17:47
Posts: 14
Role: Professional
Hi there

I was wondering if you could shed some light on how do the plugin reloading works, or maybe anyone encountered the error "The type 'MyCorePluggin.MyType' exists in both 'GamePlugin.core.dll' and 'GamePlugin.core.dll' after rebuilding CorePlugin while the editor is running?
This happens when rebuilding only Corepluggin (ie not the editor) when not doing the fullrestart

I just reproduced this on a vanilla project
(see immediate window)http://i.imgur.com/5bvBAAK.png

The code for the type YourCustomComponentType is
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Duality;
using Duality.Components.Renderers;

namespace Debug
{
   [Serializable]
    public class YourCustomComponentType : Component, ICmpUpdatable
    {
      public int MyNumber { get; set; }


      public void OnUpdate()
      {
         Log.Game.Write("updating");
      }
    }
}


Reading the error message it makes me think that perhaps an appdomain has two similar versions of the same assembly, or similar?
On the net there is not much about this error sadly.
Any info on this would be greatly appreciated :D
Cheers


Top
 Profile  
 
PostPosted: 2013/12/19, 22:25 
Site Admin
Site Admin
User avatar

Joined: 2013/05/11, 22:30
Posts: 2073
Location: Germany
Role: Professional
Hey roundcrisis,

although the error you are experiencing might look like something terrible is going on, this is actually quite normal behavior and a result of how reloading plugins in the editor works internally. I've published a blog posting about this two years ago, but here's a quick summary:

  • .Net won't let you unload Assemblies. It's impossible, because it has the potential to corrupt the state in which a .Net program is running.
  • What can be unloaded are AppDomains, but having multiple AppDomains and the overhead of expensive inter-AppDomain communication is something, a game engine like Duality can't afford on this level.
  • So, what Duality does is simply load a modified plugin Assembly another time, internally flag the old one as being disposed and reroute all Type resolving code. Technically, the old plugins are still there - they will be ghost cities and completely unused, but they won't go away until restarting.
  • However, in some cases .Net just does the Type resolve stuff itself, without letting anyone interfere. These cases are the main reason why, under some specific circumstances, a full restart is performed by the editor.

Another one of those cases is when you use typeof(XY) in a statement that is compiled at runtime, in an environment like the Immediate Window, which just grabs all the available Assemblies and uses them. Of course, there are now multiple Types with exactly the same name, because there are multiple versions of the plugin Assembly stored in memory. This is something I would've expected, but it is a rare occurrence and shouldn't be a matter in most cases.

I suspect, you still want to use the Immediate Window, though. There is a simple workaround: Just use Duality's Type resolving algorithm instead of a static typeof. It uses the same TypeId strings that are used during serialization:
Code:
Type typeA = ReflectionHelper.ResolveType("Debug.YourCustomComponentType");
Type typeB = ReflectionHelper.ResolveType("Duality.ContentRef`1[[Duality.Resources.Scene]]");
Type typeC = ReflectionHelper.ResolveType("System.Collections.Generic.Dictionary`2[[System.Int32],[System.String]]");

In case you prefer C# Code identifiers to retrieve your Types, you can use ReflectionHelper.FindCSCodeType, but you'll have to make this method public in your local branch in order to use it.

_________________
Blog | GitHub | Twitter (@Adams_Lair)


Top
 Profile  
 
PostPosted: 2013/12/19, 22:39 
Novice Member
Novice Member

Joined: 2013/05/21, 17:47
Posts: 14
Role: Professional
Hi Adam, thanks for the answer.
Yes, I saw that posting, however I didn't want to assume that you went with exactly that solution (after all, you mention this was an idea on how to implement, not the actual implementation)
The use of the immediate window is not required, it was a simple way to replicate the actual problem.

We have some editor plugins, those editor, have some parts that inherit from MemberwisePropertyEditor when the reload of the core pluggins happen they dont show up, on further examination , you get the same error as explained above. Perhaps I ll check to see if there is any reflection used (tho I dont think so) ... will keep you posted


Top
 Profile  
 
PostPosted: 2013/12/20, 00:20 
Site Admin
Site Admin
User avatar

Joined: 2013/05/11, 22:30
Posts: 2073
Location: Germany
Role: Professional
When plugin A directly references plugin B (i.e. there is a reference added in Visual Studio), and B is being reloaded from the editor at runtime, this will require a full editor restart, which should be performed automatically.

The reason for this is that static Type / Assembly references are always resolved automatically and there is no way to interfere after the initial AppDomain.AssemblyResolve event - which will yield wrong results. If you have an editor plugin that directly references a core plugin, recompiling the core plugin should trigger a full editor restart, if said editor plugin is loaded as well. If the editor doesn't restart in this case, this is probably a bug in Assembly reference detection, which needs to be fixed. (Edit: I've filed an issue report here)

Unfortunately, I haven't found a way to resolve this situation without a full system restart. Even when reloading both A and B, the internal Assembly cache of the .Net AppDomain will jump in and just resolve all the static Type references before I can do anything about it. Dynamically reloading plugins without a restart only works as long as there are no direct inter-Assembly references to the plugin.

Edit: You should, however, be able to minimize the problem by separating your plugins into distinct modules. Let's say there is a big Duality extension in CorePlugin A, and a Dualitor extension in EditorPlugin B, which builds up on A, those two will now both cause a full system restart on recompile. But if you now introduce CorePlugin C, it may reference A, derive classes from there, etc. without causing a full restart! Use it by moving all your fast-iterating and new stuff to C, while A and B provide some kind of base interface for big new concepts that you've introduced - which C can also benefit from, since it is allowed to reference A.

_________________
Blog | GitHub | Twitter (@Adams_Lair)


Top
 Profile  
 
PostPosted: 2013/12/27, 21:01 
Site Admin
Site Admin
User avatar

Joined: 2013/05/11, 22:30
Posts: 2073
Location: Germany
Role: Professional
I've fixed the issue of failing to automatically recognize the need for a full restart in a recent commit. As it turns out, dependencies between EditorPlugins and CorePlugins were never checked, only the ones between CorePlugins and other CorePlugins.

You should now experience the described restart behavior. In case this increases your iteration times too much, I've added a possible workaround to my above post, see "Edit".

_________________
Blog | GitHub | Twitter (@Adams_Lair)


Top
 Profile  
 
PostPosted: 2014/01/02, 21:10 
Novice Member
Novice Member

Joined: 2013/05/21, 17:47
Posts: 14
Role: Professional
Thanks for that Adam, I m testing a solution with multiple Appdomains to see what is the performance impact, will let you know if it goes well (or not)


Top
 Profile  
 
PostPosted: 2014/01/02, 22:23 
Site Admin
Site Admin
User avatar

Joined: 2013/05/11, 22:30
Posts: 2073
Location: Germany
Role: Professional
Sounds good. I guess, some more research never hurts. :) Here are some notes on my experience on the topic, hoping this will be helpful to you:

Disclaimer: These are all written down from the back of my mind. It's been almost two years since my research on the topic and I might remember some of it wrong.

  • Different AppDomains are separated from each other almost like different Processes are.
  • Communication between AppDomains works on a remoting basis. This means, any cross calls will result in either de/serialization or proxying of parameters.
  • Retrieving objects from a separate AppDomain (regardless whether they were created for that purpose) will yield a transparent proxy object which will reveal itself only upon attempting to reflect it, e.g. using obj.GetType() etc.
  • Reflecting on an Assembly that is only loaded on a separate AppDomain is likely and maybe even bound to loading that same Assembly in the calling AppDomain as well. In my experience, this just silently and automatically happened behind my back, just like a lot of Assembly-related logic in the .Net framework. In my own research, this was the final straw and I just gave up - because this issue may render the whole AppDomain separation pointless for Dualitys purposes.

Let me know what you find out! If there is any way to actually unload Assemblies without significant drawbacks, I'm all in.

_________________
Blog | GitHub | Twitter (@Adams_Lair)


Top
 Profile  
 
PostPosted: 2014/02/10, 13:42 
Novice Member
Novice Member

Joined: 2013/05/21, 17:47
Posts: 14
Role: Professional
HI there

A little bit of an update, I have been playing with multiple AppDomains and also with Mef trying to find a good solution for this, and so far nothing is significantly better.
MEF looked really promising because of it's catalog abstraction, I am going to try one last thing and if that doesn't improve the rebuild of core plugins workflow then I am just going to drop it
I looked at Roslyn as an alternative but not for long enough, perhaps this is a path worth investigating, perhaps when MS releases a new CPT


Cheers


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 8 posts ] 

All times are UTC + 1 hour [ DST ]


Who is online

Users browsing this forum: No registered users and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Jump to:  
cron
Powered by phpBB® Forum Software © phpBB Group