Procházet zdrojové kódy

Merge pull request #1038 from AtomicGameEngine/JME-ATOMICNET-CSCOMPONENT

CSComponent resource improvements
JoshEngebretson před 9 roky
rodič
revize
59ca8d805d

+ 1 - 1
Script/AtomicEditor/ui/frames/inspector/CSComponentClassSelector.ts

@@ -28,7 +28,7 @@ class CSComponentClassSelector extends Atomic.UIWindow {
 
         super();
 
-        var assemblyFile = component.assemblyFile;
+        var assemblyFile = component.componentFile;
 
         this.text = "Select Class: " + assemblyFile.name;
 

+ 1 - 1
Script/AtomicEditor/ui/frames/inspector/SelectionEditTypes.ts

@@ -64,7 +64,7 @@ class CSComponentEditType extends SerializableEditType {
         var csc1 = <AtomicNETScript.CSComponent>(otherType.objects[0]);
         var csc2 = <AtomicNETScript.CSComponent>(this.objects[0]);
 
-        return csc1.assemblyFile == csc2.assemblyFile && csc1.componentClassName == csc2.componentClassName;
+        return csc1.componentFile == csc2.componentFile && csc1.componentClassName == csc2.componentClassName;
 
     }
 

+ 17 - 1
Script/AtomicEditor/ui/frames/inspector/SelectionInspector.ts

@@ -176,6 +176,8 @@ class CSComponentSection extends ComponentSection {
 
         this.subscribeToEvent("CSComponentAssemblyChanged", (ev) => this.handleCSComponentAssemblyChanged(ev));
 
+        this.subscribeToEvent("CSComponentClassChanged", (ev) => this.handleCSComponentClassChanged(ev));
+
     }
 
     private handleCSComponentAssemblyChanged(ev) {
@@ -185,7 +187,7 @@ class CSComponentSection extends ComponentSection {
         if (!csc)
           return;
 
-        if (csc.assemblyFile == <AtomicNETScript.CSComponentAssembly> ev.resource) {
+        if (csc.componentFile == <Atomic.ScriptComponentFile> ev.resource) {
 
           var attrInfos = csc.getAttributes();
           this.updateDynamicAttrInfos(attrInfos);
@@ -194,6 +196,20 @@ class CSComponentSection extends ComponentSection {
 
     }
 
+    private handleCSComponentClassChanged(ev) {
+
+        var csc = <AtomicNETScript.CSComponent>this.editType.getFirstObject();
+
+        if (!csc)
+          return;
+
+        var attrInfos = csc.getAttributes();
+        this.updateDynamicAttrInfos(attrInfos);
+        this.updateTextFromClassAttr();
+
+    }
+
+
     private handleAttributeEditResourceChanged(ev: AttributeEditResourceChangedEvent) {
 
 

+ 13 - 20
Script/AtomicNET/AtomicNET/Scene/CSComponentCore.cs

@@ -298,25 +298,17 @@ namespace AtomicEngine
 
         void HandleComponentLoad(uint eventType, ScriptVariantMap eventData)
         {
-            var assemblyPath = eventData["AssemblyPath"];
-
             var className = eventData["ClassName"];
+
             IntPtr csnative = eventData.GetVoidPtr("NativeInstance");
             IntPtr fieldValues = IntPtr.Zero;
 
             if (eventData.Contains("FieldValues"))
                 fieldValues = eventData.GetVoidPtr("FieldValues");
 
-            Dictionary<string, CSComponentInfo> assemblyTypes = null;
-
-            if (!componentCache.TryGetValue(assemblyPath, out assemblyTypes))
-            {
-                return;
-            }
-
             CSComponentInfo csinfo;
 
-            if (!assemblyTypes.TryGetValue(className, out csinfo))
+            if (!componentCache.TryGetValue(className, out csinfo))
             {
                 return;
             }
@@ -332,14 +324,12 @@ namespace AtomicEngine
 
         }
 
+        [Obsolete("Method HandleComponentAssemblyReference is deprecated (loading component assemblies at runtime, will be changed to preload them)")]
         void HandleComponentAssemblyReference(uint eventType, ScriptVariantMap eventData)
         {
 #if ATOMIC_DESKTOP || ATOMIC_MOBILE
             string assemblyPath = eventData["AssemblyPath"];
-
             string assemblyName = Path.GetFileNameWithoutExtension(assemblyPath);
-            if (componentCache.ContainsKey(assemblyName))
-                return;
 
             Assembly assembly = Assembly.LoadFrom(assemblyPath);
 
@@ -363,15 +353,14 @@ namespace AtomicEngine
         void ParseAssembly(Assembly assembly)
         {
 #if ATOMIC_DESKTOP || ATOMIC_MOBILE
-            String assemblyPath = assembly.GetName().Name;
-
-            Dictionary<string, CSComponentInfo> assemblyTypes = null;
 
-            if (!componentCache.TryGetValue(assemblyPath, out assemblyTypes))
+            if (parsedAssemblies.ContainsKey(assembly))
             {
-                componentCache[assemblyPath] = assemblyTypes = new Dictionary<string, CSComponentInfo>();
+                return;
             }
 
+            parsedAssemblies[assembly] = true;
+
             Type[] types = assembly.GetTypes();
 
             foreach (var type in types)
@@ -380,7 +369,7 @@ namespace AtomicEngine
                 {
                     var csinfo = new CSComponentInfo(type);
                     csinfoLookup[csinfo.Type] = csinfo;
-                    assemblyTypes[type.Name] = csinfo;
+                    componentCache[type.Name] = csinfo;
                 }
             }
 #endif
@@ -403,7 +392,11 @@ namespace AtomicEngine
 
         }
 
-        Dictionary<string, Dictionary<string, CSComponentInfo>> componentCache = new Dictionary<string, Dictionary<string, CSComponentInfo>>();
+        // type name -> CSComponentInfo lookup TODO: store with namespace to solve ambiguities
+        Dictionary<string, CSComponentInfo> componentCache = new Dictionary<string, CSComponentInfo>();
+
+        [Obsolete("Member parsedAssemblies is temporarily required for runtime component assemblies loading")]
+        Dictionary<Assembly, bool> parsedAssemblies = new Dictionary<Assembly, bool>();
 
         Dictionary<Type, CSComponentInfo> csinfoLookup = new Dictionary<Type, CSComponentInfo>();
 

+ 5 - 0
Source/Atomic/Script/ScriptComponentFile.h

@@ -40,6 +40,8 @@ public:
 
     static void RegisterObject(Context* context);
 
+    /// Only valid in editor, as we don't inspect classnames at runtime
+    virtual const Vector<String>& GetClassNames() { return classNames_; }
     const EnumMap& GetEnums(const String& classname = String::EMPTY) const;
     const FieldMap& GetFields(const String& classname = String::EMPTY) const;
     const VariantMap& GetDefaultFieldValues(const String& classname = String::EMPTY) const;
@@ -54,6 +56,9 @@ protected:
     void AddField(const String& fieldName, VariantType variantType, const String& classname = String::EMPTY);
     void AddDefaultValue(const String& fieldName, const Variant& value, const String& classname = String::EMPTY);
 
+    // only valid in editor
+    Vector<String> classNames_;
+
 private:
 
     ClassFieldMap classFields_;

+ 2 - 0
Source/AtomicEditor/EditorMode/AEEditorMode.cpp

@@ -180,7 +180,9 @@ bool EditorMode::PlayProject(String addArgs, bool debug)
     paths.Push(project->GetResourcePath());
 
     // fixme: this is for loading from cache
+    // https://github.com/AtomicGameEngine/AtomicGameEngine/issues/1037
     paths.Push(project->GetProjectPath());
+
     paths.Push(project->GetProjectPath() + "Cache");
 
     String resourcePaths;

+ 4 - 1
Source/AtomicNET/NETNative/Desktop/NETIPCPlayerApp.cpp

@@ -23,9 +23,9 @@
 
 #include <Atomic/Engine/Engine.h>
 #include <Atomic/IO/FileSystem.h>
-
 #include "NETCore.h"
 #include <AtomicNET/NETScript/NETScript.h>
+#include <AtomicNET/NETScript/CSComponentAssembly.h>
 
 #include "NETIPCPlayerApp.h"
 
@@ -75,6 +75,9 @@ namespace Atomic
             return exitCode_;
         }
 
+        // TODO: Proper CSComponent assembly preload (this only works on desktop)
+        CSComponentAssembly::PreloadClassAssemblies();
+
         Start();
 
         if (exitCode_)

+ 18 - 18
Source/AtomicNET/NETScript/CSComponent.cpp

@@ -56,8 +56,6 @@ void CSComponent::RegisterObject(Context* context)
 
     ATOMIC_ATTRIBUTE("FieldValues", VariantMap, fieldValues_, Variant::emptyVariantMap, AM_FILE);
 
-    ATOMIC_MIXED_ACCESSOR_ATTRIBUTE("Assembly", GetAssemblyFileAttr, SetAssemblyFileAttr, ResourceRef, ResourceRef(CSComponentAssembly::GetTypeStatic()), AM_DEFAULT);
-
     ATOMIC_ACCESSOR_ATTRIBUTE("Class", GetComponentClassName, SetComponentClassName, String, String::EMPTY, AM_DEFAULT);
 
 }
@@ -78,17 +76,18 @@ void CSComponent::ApplyFieldValues()
 
 void CSComponent::SetComponentClassName(const String& name)
 {
+    if (componentClassName_ == name)
+        return;
+
     componentClassName_ = name;
 
-    if (assemblyFile_ && assemblyFile_->GetClassNames().Contains(name))
+    if (context_->GetEditorContext())
     {
-        /*
         using namespace CSComponentClassChanged;
         VariantMap eventData;
         eventData[P_CSCOMPONENT] = this;
         eventData[P_CLASSNAME] = name;
         SendEvent(E_CSCOMPONENTCLASSCHANGED, eventData);
-        */
     }
 }
 
@@ -104,14 +103,22 @@ void CSComponent::OnSceneSet(Scene* scene)
 
 void CSComponent::SendLoadEvent()
 {
-    if (!assemblyFile_ || !componentClassName_.Length())
+    if (!componentClassName_.Length())
         return;
 
+    // TODO: We need to factor out runtime loading of CSComponent assemblies
+    CSComponentAssembly*  assemblyFile = 0;
+
+#ifdef ATOMIC_PLATFORM_DESKTOP
+    assemblyFile = CSComponentAssembly::ResolveClassAssembly(componentClassName_);
+#endif
+
     using namespace CSComponentLoad;
 
     VariantMap eventData;
 
-    eventData[P_ASSEMBLYPATH] = GetFileName(assemblyFile_->GetFullPath());
+    eventData[P_ASSEMBLYPATH] = assemblyFile ? GetFileName(assemblyFile->GetFullPath()) : String::EMPTY;
+
     eventData[P_CLASSNAME] = componentClassName_;
     eventData[P_NATIVEINSTANCE] = (void*) this;
 
@@ -142,20 +149,13 @@ bool CSComponent::LoadXML(const XMLElement& source, bool setInstanceDefault)
     return success;
 }
 
-void CSComponent::SetAssemblyFile(CSComponentAssembly* assemblyFile)
+ScriptComponentFile* CSComponent::GetComponentFile()
 {
-    assemblyFile_ = assemblyFile;
-}
+    if (!componentClassName_.Length())
+        return 0;
 
-ResourceRef CSComponent::GetAssemblyFileAttr() const
-{
-    return GetResourceRef(assemblyFile_, CSComponentAssembly::GetTypeStatic());
-}
+    return CSComponentAssembly::ResolveClassAssembly(componentClassName_);
 
-void CSComponent::SetAssemblyFileAttr(const ResourceRef& value)
-{
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    SetAssemblyFile(cache->GetResource<CSComponentAssembly>(value.name_));
 }
 
 

+ 1 - 8
Source/AtomicNET/NETScript/CSComponent.h

@@ -60,13 +60,7 @@ public:
     void SetComponentClassName(const String& name);
     const String& GetComponentClassName() const { return componentClassName_; }
 
-    virtual ScriptComponentFile* GetComponentFile() { return assemblyFile_; }
-
-    CSComponentAssembly* GetAssemblyFile() { return assemblyFile_; }
-    void SetAssemblyFile(CSComponentAssembly* assemblyFile);
-
-    ResourceRef GetAssemblyFileAttr() const;
-    void SetAssemblyFileAttr(const ResourceRef& value);
+    virtual ScriptComponentFile* GetComponentFile();
 
 protected:
 
@@ -80,7 +74,6 @@ private:
     void SendLoadEvent();
 
     String componentClassName_;
-    SharedPtr<CSComponentAssembly> assemblyFile_;
 
 };
 

+ 98 - 1
Source/AtomicNET/NETScript/CSComponentAssembly.cpp

@@ -27,6 +27,7 @@
 #include <Atomic/Core/Profiler.h>
 #include <Atomic/Resource/ResourceCache.h>
 #include <Atomic/IO/Serializer.h>
+#include <Atomic/Script/ScriptSystem.h>
 
 #include "NETScriptEvents.h"
 #include "CSComponentAssembly.h"
@@ -231,7 +232,10 @@ namespace Atomic
 
     bool CSComponentAssembly::BeginLoad(Deserializer& source)
     {
-        fullAssemblyPath_ = source.GetName();
+        // TODO: Assemblies in packages?
+        File* sourceFile = (File*) &source;
+
+        fullAssemblyPath_ = sourceFile->GetFullPath();
 
         VariantMap eventData;
 
@@ -248,4 +252,97 @@ namespace Atomic
         return true;
     }
 
+    CSComponentAssembly* CSComponentAssembly::ResolveClassAssembly(const String& fullClassName)
+    {
+        Context* context = ScriptSystem::GetContext();
+        assert(context);
+
+        String classname = fullClassName;
+        String csnamespace;
+
+        // Handle namespaces
+        if (fullClassName.Contains('.'))
+        {
+
+            StringVector elements = fullClassName.Split('.');
+
+            if (elements.Size() <= 1)
+                return 0;
+
+            classname = elements.Back();
+            elements.Pop();
+
+            csnamespace = String::Joined(elements, ".");
+        }
+
+        ResourceCache* cache = context->GetSubsystem<ResourceCache>();
+
+        PODVector<CSComponentAssembly*> assemblies;
+
+        cache->GetResources<CSComponentAssembly>(assemblies);
+
+        for (unsigned i = 0; i < assemblies.Size(); i++)
+        {
+            CSComponentAssembly* assembly = assemblies[i];
+
+            // TODO: support namespaces
+            const StringVector& classNames = assembly->GetClassNames();
+            if (classNames.Contains(classname))
+            {
+                return assembly;
+            }
+
+        }
+
+        return 0;
+
+    }
+
+    bool CSComponentAssembly::PreloadClassAssemblies()
+    {
+        // TEMPORARY SOLUTION, Desktop only
+
+        ATOMIC_LOGINFO("Preloading Class Assemblies");
+
+        Context* context = ScriptSystem::GetContext();
+        assert(context);
+
+        ResourceCache* cache = context->GetSubsystem<ResourceCache>();
+        FileSystem* fileSystem = context->GetSubsystem<FileSystem>();
+
+        const StringVector& resourceDirs = cache->GetResourceDirs();
+
+        for (unsigned i = 0; i < resourceDirs.Size(); i++)
+        {
+            const String& resourceDir = resourceDirs[i];
+
+            ATOMIC_LOGINFOF("Scanning: %s", resourceDir.CString());
+
+            StringVector results;
+            fileSystem->ScanDir(results, resourceDir, "*.dll", SCAN_FILES, true);
+
+            for (unsigned j = 0; j < results.Size(); j++)
+            {
+                // FIXME: This filtering is necessary as we're loading setting project root folder as a resource dir
+                // https://github.com/AtomicGameEngine/AtomicGameEngine/issues/1037
+
+                String filter = results[j].ToLower();
+
+                if (filter.StartsWith("atomicnet/") || filter.StartsWith("resources/"))
+                {
+                    ATOMIC_LOGINFOF("Skipping Assembly: %s (https://github.com/AtomicGameEngine/AtomicGameEngine/issues/1037)", results[j].CString());
+                    continue;
+                }
+
+                ATOMIC_LOGINFOF("Loading Assembly: %s", results[j].CString());
+
+                cache->GetResource<CSComponentAssembly>(results[j]);
+            }
+
+        }
+
+        return true;
+
+    }
+
 }

+ 6 - 3
Source/AtomicNET/NETScript/CSComponentAssembly.h

@@ -64,6 +64,12 @@ namespace Atomic
         /// Only valid in editor, as we don't inspect assembly at runtime
         const Vector<String>& GetClassNames() { return classNames_; }
 
+        // Find assembly by class name or namespace qualified classname
+        static CSComponentAssembly* ResolveClassAssembly(const String& fullClassName);
+
+        // TODO: Proper method to preload class assemblies (which will also work on mobile)
+        static bool PreloadClassAssemblies();
+
     private:
 
         static void InitTypeMap();
@@ -72,9 +78,6 @@ namespace Atomic
 
         String fullAssemblyPath_;
 
-        // only valid in editor
-        Vector<String> classNames_;
-
         HashMap<String, Vector<EnumInfo>> assemblyEnums_;
         static HashMap<StringHash, VariantType> typeMap_;
 

+ 5 - 0
Source/AtomicNET/NETScript/NETScriptEvents.h

@@ -47,5 +47,10 @@ namespace Atomic
         ATOMIC_PARAM(P_ASSEMBLYPATH, AssemblyPath); // String
     }
 
+    ATOMIC_EVENT(E_CSCOMPONENTCLASSCHANGED, CSComponentClassChanged)
+    {
+        ATOMIC_PARAM(P_CSCOMPONENT, Component); // CSComponent*
+        ATOMIC_PARAM(P_CLASSNAME, Classname); // String
+    }
 
 }