Browse Source

Added UnknownComponent mechanism for dealing with unregistered components in scene load/save. For now attributes are not editable. Related to issue #149.

Lasse Öörni 12 years ago
parent
commit
dd075db33b

+ 2 - 2
Source/Engine/Core/Context.cpp

@@ -172,10 +172,10 @@ Object* Context::GetEventSender() const
         return 0;
 }
 
-const String& Context::GetTypeName(ShortStringHash type) const
+const String& Context::GetTypeName(ShortStringHash objectType) const
 {
     // Search factories to find the hash-to-name mapping
-    HashMap<ShortStringHash, SharedPtr<ObjectFactory> >::ConstIterator i = factories_.Find(type);
+    HashMap<ShortStringHash, SharedPtr<ObjectFactory> >::ConstIterator i = factories_.Find(objectType);
     return i != factories_.End() ? i->second_->GetTypeName() : String::EMPTY;
 }
 

+ 1 - 1
Source/Engine/Core/Context.h

@@ -87,7 +87,7 @@ public:
     /// Return active event handler. Set by Object. Null outside event handling.
     EventHandler* GetEventHandler() const { return eventHandler_; }
     /// Return object type name from hash, or empty if unknown.
-    const String& GetTypeName(ShortStringHash type) const;
+    const String& GetTypeName(ShortStringHash objectType) const;
     /// Return a specific attribute description for an object, or null if not found.
     AttributeInfo* GetAttribute(ShortStringHash objectType, const char* name);
     /// Template version of returning a subsystem.

+ 26 - 5
Source/Engine/Scene/Node.cpp

@@ -30,6 +30,7 @@
 #include "Scene.h"
 #include "SceneEvents.h"
 #include "SmoothedTransform.h"
+#include "UnknownComponent.h"
 #include "XMLFile.h"
 
 #include "DebugNew.h"
@@ -122,7 +123,7 @@ bool Node::Save(Serializer& dest) const
         if (component->IsTemporary())
             continue;
         
-        // Create a separate buffer to be able to skip unknown components during deserialization
+        // Create a separate buffer to be able to skip failing components during deserialization
         VectorBuffer compBuffer;
         if (!component->Save(compBuffer))
             return false;
@@ -1056,7 +1057,8 @@ bool Node::Load(Deserializer& source, SceneResolver& resolver, bool readChildren
         VectorBuffer compBuffer(source, source.ReadVLE());
         ShortStringHash compType = compBuffer.ReadShortStringHash();
         unsigned compID = compBuffer.ReadUInt();
-        Component* newComponent = CreateComponent(compType,
+        
+        Component* newComponent = SafeCreateComponent(String::EMPTY, compType,
             (mode == REPLICATED && compID < FIRST_LOCAL_ID) ? REPLICATED : LOCAL, rewriteIDs ? 0 : compID);
         if (newComponent)
         {
@@ -1097,7 +1099,7 @@ bool Node::LoadXML(const XMLElement& source, SceneResolver& resolver, bool readC
     {
         String typeName = compElem.GetAttribute("type");
         unsigned compID = compElem.GetInt("id");
-        Component* newComponent = CreateComponent(typeName,
+        Component* newComponent = SafeCreateComponent(typeName, ShortStringHash(typeName),
             (mode == REPLICATED && compID < FIRST_LOCAL_ID) ? REPLICATED : LOCAL, rewriteIDs ? 0 : compID);
         if (newComponent)
         {
@@ -1349,6 +1351,25 @@ unsigned Node::GetNumPersistentComponents() const
     return ret;
 }
 
+Component* Node::SafeCreateComponent(const String& typeName, ShortStringHash type, CreateMode mode, unsigned id)
+{
+    // First check if factory for type exists
+    if (!context_->GetTypeName(type).Empty())
+        return CreateComponent(type, mode, id);
+    else
+    {
+        // Else create as UnknownComponent
+        SharedPtr<UnknownComponent> newComponent(new UnknownComponent(context_));
+        if (typeName.Empty() || typeName.StartsWith("Unknown", false))
+            newComponent->SetType(type);
+        else
+            newComponent->SetTypeName(typeName);
+        
+        AddComponent(newComponent, id, mode);
+        return newComponent;
+    }
+}
+
 void Node::UpdateWorldTransform() const
 {
     Matrix3x4 transform = GetTransform();
@@ -1443,8 +1464,8 @@ Node* Node::CloneRecursive(Node* parent, SceneResolver& resolver, CreateMode mod
     for (Vector<SharedPtr<Component> >::ConstIterator i = components_.Begin(); i != components_.End(); ++i)
     {
         Component* component = *i;
-        Component* cloneComponent = cloneNode->CreateComponent(component->GetType(), (mode == REPLICATED && component->GetID() <
-            FIRST_LOCAL_ID) ? REPLICATED : LOCAL);
+        Component* cloneComponent = cloneNode->SafeCreateComponent(component->GetTypeName(), component->GetType(), 
+            (mode == REPLICATED && component->GetID() < FIRST_LOCAL_ID) ? REPLICATED : LOCAL, 0);
         if (!cloneComponent)
         {
             LOGERROR("Could not clone component " + component->GetTypeName());

+ 2 - 0
Source/Engine/Scene/Node.h

@@ -375,6 +375,8 @@ protected:
     VariantMap vars_;
 
 private:
+    /// Create component, allowing UnknownComponent if actual type is not supported. Leave typeName empty if not known.
+    Component* SafeCreateComponent(const String& typeName, ShortStringHash type, CreateMode mode, unsigned id);
     /// Recalculate the world transform.
     void UpdateWorldTransform() const;
     /// Remove child node by iterator.

+ 2 - 1
Source/Engine/Scene/Scene.cpp

@@ -21,7 +21,6 @@
 //
 
 #include "Precompiled.h"
-#include "Component.h"
 #include "Context.h"
 #include "CoreEvents.h"
 #include "File.h"
@@ -33,6 +32,7 @@
 #include "SceneEvents.h"
 #include "SmoothedTransform.h"
 #include "Spline.h"
+#include "UnknownComponent.h"
 #include "WorkQueue.h"
 #include "XMLFile.h"
 
@@ -957,6 +957,7 @@ void RegisterSceneLibrary(Context* context)
     Scene::RegisterObject(context);
     SmoothedTransform::RegisterObject(context);
     Spline::RegisterObject(context);
+    UnknownComponent::RegisterObject(context);
 }
 
 }

+ 132 - 0
Source/Engine/Scene/UnknownComponent.cpp

@@ -0,0 +1,132 @@
+//
+// Copyright (c) 2008-2013 the Urho3D project.
+//
+// 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.
+//
+
+#include "Precompiled.h"
+#include "Context.h"
+#include "Deserializer.h"
+#include "Log.h"
+#include "Serializer.h"
+#include "UnknownComponent.h"
+#include "XMLElement.h"
+
+namespace Urho3D
+{
+
+UnknownComponent::UnknownComponent(Context* context) :
+    Component(context),
+    useXML_(false)
+{
+}
+
+void UnknownComponent::RegisterObject(Context* context)
+{
+    context->RegisterFactory<UnknownComponent>();
+}
+
+bool UnknownComponent::Load(Deserializer& source, bool setInstanceDefault)
+{
+    useXML_ = false;
+    
+    // Assume we are reading from a component data buffer, and the type has already been read
+    unsigned dataSize = source.GetSize() - source.GetPosition();
+    binaryAttributes_.Resize(dataSize);
+    return dataSize ? source.Read(&binaryAttributes_[0], dataSize) == dataSize : true;
+}
+
+bool UnknownComponent::LoadXML(const XMLElement& source, bool setInstanceDefault)
+{
+    useXML_ = true;
+    
+    XMLElement attrElem = source.GetChild("attribute");
+    while (attrElem)
+    {
+        Pair<String, String> attr;
+        attr.first_ = attrElem.GetAttribute("name");
+        attr.second_ = attrElem.GetAttribute("value");
+        if (!attr.first_.Empty())
+            xmlAttributes_.Push(attr);
+        
+        attrElem = attrElem.GetNext("attribute");
+    }
+    
+    return true;
+}
+
+bool UnknownComponent::Save(Serializer& dest) const
+{
+    if (useXML_)
+        LOGWARNING("UnknownComponent loaded in XML mode, attributes will be empty for binary save");
+    
+    // Write type and ID
+    if (!dest.WriteShortStringHash(GetType()))
+        return false;
+    if (!dest.WriteUInt(id_))
+        return false;
+    
+    if (!binaryAttributes_.Size())
+        return true;
+    else
+        return dest.Write(&binaryAttributes_[0], binaryAttributes_.Size()) == binaryAttributes_.Size();
+}
+
+bool UnknownComponent::SaveXML(XMLElement& dest) const
+{
+    if (dest.IsNull())
+    {
+        LOGERROR("Could not save " + GetTypeName() + ", null destination element");
+        return false;
+    }
+    
+    if (!useXML_)
+        LOGWARNING("UnknownComponent loaded in binary mode, attributes will be empty for XML save");
+    
+    // Write type and ID
+    if (!dest.SetString("type", GetTypeName()))
+        return false;
+    if (!dest.SetInt("id", id_))
+        return false;
+    
+    for (unsigned i = 0; i < xmlAttributes_.Size(); ++i)
+    {
+        const Pair<String, String>& attr = xmlAttributes_[i];
+        
+        XMLElement attrElem = dest.CreateChild("attribute");
+        attrElem.SetAttribute("name", attr.first_);
+        attrElem.SetAttribute("value", attr.second_);
+    }
+    
+    return true;
+}
+
+void UnknownComponent::SetTypeName(const String& typeName)
+{
+    typeName_ = typeName;
+    typeHash_ = typeName;
+}
+
+void UnknownComponent::SetType(ShortStringHash typeHash)
+{
+    typeName_ = "Unknown " + typeHash.ToString();
+    typeHash_ = typeHash;
+}
+
+}

+ 83 - 0
Source/Engine/Scene/UnknownComponent.h

@@ -0,0 +1,83 @@
+//
+// Copyright (c) 2008-2013 the Urho3D project.
+//
+// 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.
+//
+
+#pragma once
+
+#include "Component.h"
+
+namespace Urho3D
+{
+
+/// Placeholder for allowing unregistered components to be loaded & saved along with scenes.
+class URHO3D_API UnknownComponent : public Component
+{
+public:
+    /// Construct.
+    UnknownComponent(Context* context);
+    
+    /// Register object factory.
+    static void RegisterObject(Context* context);
+    
+    /// Return type of the stored component.
+    virtual ShortStringHash GetType() const { return typeHash_; }
+    /// Return type name of the stored component.
+    virtual const String& GetTypeName() const { return typeName_; }
+    /// Load from binary data. Return true if successful.
+    virtual bool Load(Deserializer& source, bool setInstanceDefault = false);
+    /// Load from XML data. Return true if successful.
+    virtual bool LoadXML(const XMLElement& source, bool setInstanceDefault = false);
+    /// Save as binary data. Return true if successful.
+    virtual bool Save(Serializer& dest) const;
+    /// Save as XML data. Return true if successful.
+    virtual bool SaveXML(XMLElement& dest) const;
+    
+    /// Initialize the type name. Called by Node when loading.
+    void SetTypeName(const String& typeName);
+   /// Initialize the type hash only when type name not known. Called by Node when loading.
+    void SetType(ShortStringHash typeHash);
+    
+    /// Return the XML format attributes. Empty when loaded with binary serialization.
+    const Vector<Pair<String, String> >& GetXMLAttributes() const { return xmlAttributes_; }
+    /// Return the binary attributes. Empty when loaded with XML serialization.
+    const PODVector<unsigned char>& GetBinaryAttributes() const { return binaryAttributes_; }
+    /// Return whether was loaded using XML data.
+    bool GetUseXML() const { return useXML_; }
+    
+    /// Return static type.
+    static Urho3D::ShortStringHash GetTypeStatic() { static const ShortStringHash typeStatic("UnknownComponent"); return typeStatic; } \
+    /// Return static type name.
+    static const Urho3D::String& GetTypeNameStatic() { static const String typeNameStatic("UnknownComponent"); return typeNameStatic; } \
+    
+private:
+    /// Type of stored component.
+    ShortStringHash typeHash_;
+    /// Type name of the stored component.
+    String typeName_;
+    /// XML format attributes.
+    Vector<Pair<String, String> > xmlAttributes_;
+    /// Binary attributes.
+    PODVector<unsigned char> binaryAttributes_;
+    /// Flag of whether was loaded using XML data.
+    bool useXML_;
+};
+
+}