mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-05 13:17:49 +00:00
779 lines
25 KiB
C++
779 lines
25 KiB
C++
/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */
|
|
|
|
#pragma once
|
|
|
|
#include "Reflection/Reflection.h"
|
|
#include "Reflection/Event.h"
|
|
#include "V8Tree/Property.h"
|
|
#include "V8Xml/Reference.h"
|
|
#include "V8Tree/Verb.h"
|
|
|
|
#include "rbx/Countable.h"
|
|
#include "Util/Guid.h"
|
|
|
|
#include <vector>
|
|
#include <string>
|
|
#include "boost/weak_ptr.hpp"
|
|
#include "boost/shared_ptr.hpp"
|
|
#include "boost/enable_shared_from_this.hpp"
|
|
#include <boost/static_assert.hpp>
|
|
#include <boost/flyweight.hpp>
|
|
|
|
namespace RBX {
|
|
|
|
class Instance;
|
|
|
|
// Convenience class
|
|
template <
|
|
class Class,
|
|
class BaseClass,
|
|
const char* const& sClassName,
|
|
Reflection::ClassDescriptor::Functionality functionality = Reflection::ClassDescriptor::PERSISTENT,
|
|
Security::Permissions security = Security::None
|
|
>
|
|
class DescribedCreatable : public Reflection::Described<Class, sClassName, FactoryProduct<Class, BaseClass, sClassName, Instance>, functionality, security>
|
|
{
|
|
protected:
|
|
inline DescribedCreatable() {}
|
|
template<class Arg0>
|
|
inline DescribedCreatable(Arg0 arg0):Reflection::Described<Class, sClassName, FactoryProduct<Class, BaseClass, sClassName, Instance>, functionality, security>(arg0) {}
|
|
template<class Arg0, class Arg1>
|
|
inline DescribedCreatable(Arg0 arg0, Arg1 arg1):Reflection::Described<Class, sClassName, FactoryProduct<Class, BaseClass, sClassName, Instance>, functionality, security>(arg0, arg1) {}
|
|
};
|
|
|
|
// Convenience class
|
|
template <
|
|
class Class,
|
|
class BaseClass,
|
|
const char* const& sClassName,
|
|
Reflection::ClassDescriptor::Functionality functionality = Reflection::ClassDescriptor::PERSISTENT,
|
|
Security::Permissions security = Security::None
|
|
>
|
|
class DescribedNonCreatable : public Reflection::Described<Class, sClassName, NonFactoryProduct<BaseClass, sClassName>, functionality, security>
|
|
{
|
|
protected:
|
|
inline DescribedNonCreatable() {}
|
|
template<class Arg0>
|
|
inline DescribedNonCreatable(Arg0 arg0):Reflection::Described<Class, sClassName, NonFactoryProduct<BaseClass, sClassName>, functionality, security>(arg0) {}
|
|
template<class Arg0, class Arg1>
|
|
inline DescribedNonCreatable(Arg0 arg0, Arg1 arg1):Reflection::Described<Class, sClassName, NonFactoryProduct<BaseClass, sClassName>, functionality, security>(arg0, arg1) {}
|
|
template<class Arg0, class Arg1, class Arg2>
|
|
inline DescribedNonCreatable(Arg0 arg0, Arg1 arg1, Arg2 arg2):Reflection::Described<Class, sClassName, NonFactoryProduct<BaseClass, sClassName>, functionality, security>(arg0, arg1, arg2) {}
|
|
template<class Arg0, class Arg1, class Arg2, class Arg3>
|
|
inline DescribedNonCreatable(Arg0 arg0, Arg1 arg1, Arg2 arg2, Arg3 arg3):Reflection::Described<Class, sClassName, NonFactoryProduct<BaseClass, sClassName>, functionality, security>(arg0, arg1, arg2, arg3) {}
|
|
};
|
|
|
|
class Instance;
|
|
class ServiceProvider;
|
|
|
|
|
|
/// A child has been added to the Notifier
|
|
struct ChildAdded {
|
|
public:
|
|
shared_ptr<Instance> const child;
|
|
ChildAdded(Instance* child);
|
|
ChildAdded(const ChildAdded& event);
|
|
private:
|
|
ChildAdded& operator=(const ChildAdded&);
|
|
};
|
|
|
|
/// A child has been removed from the Notifier
|
|
struct ChildRemoved {
|
|
public:
|
|
shared_ptr<Instance> const child;
|
|
ChildRemoved(Instance* child);
|
|
ChildRemoved(const ChildRemoved& event);
|
|
private:
|
|
ChildRemoved& operator=(const ChildAdded&);
|
|
};
|
|
|
|
/// A descendant has been added to the Notifier
|
|
struct DescendantAdded {
|
|
friend class Instance;
|
|
public:
|
|
shared_ptr<Instance> const instance;
|
|
shared_ptr<Instance> const parent; // The direct parent of the descendant
|
|
private:
|
|
DescendantAdded(shared_ptr<Instance> instance, shared_ptr<Instance> parent)
|
|
:instance(instance),parent(parent)
|
|
{}
|
|
DescendantAdded(Instance* instance, Instance* parent)
|
|
:instance(shared_from(instance)),parent(shared_from(parent))
|
|
{}
|
|
};
|
|
|
|
/// A descendant of the Notifier is about to be removed
|
|
struct DescendantRemoving {
|
|
public:
|
|
shared_ptr<Instance> const instance;
|
|
shared_ptr<Instance> const parent; // The direct parent of the descendant before it is removed
|
|
DescendantRemoving(const shared_ptr<Instance>& instance, const shared_ptr<Instance>& parent)
|
|
:instance(instance),parent(parent)
|
|
{}
|
|
};
|
|
|
|
struct AncestorChanged {
|
|
public:
|
|
Instance* const child;
|
|
Instance* const oldParent;
|
|
Instance* const newParent;
|
|
AncestorChanged(Instance* child, Instance* oldParent, Instance* newParent)
|
|
:child(child),oldParent(oldParent),newParent(newParent)
|
|
{}
|
|
};
|
|
|
|
extern const char* const sInstance;
|
|
|
|
typedef std::vector<shared_ptr<Instance> > Instances;
|
|
|
|
class OnDemandInstance : public Allocator<OnDemandInstance>
|
|
{
|
|
public:
|
|
rbx::signal<void(shared_ptr<Instance>)> childAddedSignal;
|
|
rbx::signal<void(shared_ptr<Instance>)> childRemovedSignal;
|
|
rbx::signal<void(shared_ptr<Instance>)> descendantAddedSignal;
|
|
rbx::signal<void(shared_ptr<Instance>)> descendantRemovingSignal;
|
|
rbx::signal<void(shared_ptr<Instance>)> instanceClonedSignal;
|
|
|
|
struct ThreadWaitingForChild
|
|
{
|
|
std::string childName;
|
|
boost::function<void(shared_ptr<Instance>)> resumeFunction;
|
|
};
|
|
std::vector<ThreadWaitingForChild> threadsWaitingForChildren;
|
|
|
|
virtual ~OnDemandInstance(){};
|
|
};
|
|
|
|
class Instance
|
|
: public Reflection::Described<Instance, sInstance>
|
|
, public GuidItem<Instance>
|
|
, public Diagnostics::Countable<Instance>
|
|
, public boost::noncopyable
|
|
{
|
|
friend class SetParentSentry;
|
|
private:
|
|
static void predelete(Instance* instance);
|
|
void predelete();
|
|
friend class Creatable<Instance>::Deleter;
|
|
|
|
bool archivable;
|
|
bool isParentLocked;
|
|
bool robloxLocked;
|
|
bool isSettingParent;
|
|
|
|
boost::flyweight<std::string> name;
|
|
|
|
copy_on_write_ptr<Instances> children;
|
|
Instance* parent; // this field is set after initialization by Instance::addChild
|
|
|
|
protected:
|
|
Instance();
|
|
Instance(const char* name);
|
|
|
|
// Destructor is protected. Call "destroy(instance)" instead of "delete instance".
|
|
virtual ~Instance();
|
|
|
|
boost::scoped_ptr<OnDemandInstance> onDemandPtr;
|
|
|
|
virtual OnDemandInstance* initOnDemand();
|
|
|
|
public:
|
|
const OnDemandInstance* onDemandRead() const;
|
|
OnDemandInstance* onDemandWrite();
|
|
|
|
virtual void onGuidChanged();
|
|
|
|
void lockParent() { isParentLocked = true; }
|
|
void unlockParent() { isParentLocked = false; }
|
|
bool getIsParentLocked() const { return isParentLocked; }
|
|
void securityCheck() const;
|
|
void securityCheck(RBX::Security::Context& context) const;
|
|
|
|
static Reflection::PropDescriptor<Instance, bool> propArchivable;
|
|
bool getIsArchivable() const { return archivable; }
|
|
virtual void setIsArchivable(bool value);
|
|
|
|
// Call "destroy" to delete an Instance fully so it can't be used again
|
|
virtual void destroy();
|
|
void remove();
|
|
|
|
enum SaveFilter
|
|
{
|
|
SAVE_WORLD = 0,
|
|
SAVE_GAME = 1,
|
|
SAVE_ALL = 2,
|
|
};
|
|
|
|
static const Reflection::PropDescriptor<Instance, std::string> desc_Name;
|
|
static const Reflection::RefPropDescriptor<Instance, Instance> propParent;
|
|
static const Reflection::PropDescriptor<Instance, bool> propRobloxLocked;
|
|
|
|
enum CombinedSignalType
|
|
{
|
|
CHILD_ADDED,
|
|
CHILD_REMOVED,
|
|
PROPERTY_CHANGED,
|
|
EVENT_INVOCATION,
|
|
OUTFIT_CHANGED,
|
|
ANCESTRY_CHANGED,
|
|
CLUMP_CHANGED,
|
|
SLEEPING_CHANGED,
|
|
HUMANOID_CHANGED
|
|
};
|
|
|
|
class ICombinedSignalData
|
|
{
|
|
public:
|
|
virtual ~ICombinedSignalData() {};
|
|
};
|
|
|
|
class ChildAddedSignalData : public ICombinedSignalData
|
|
{
|
|
public:
|
|
ChildAddedSignalData(const shared_ptr<Instance>& child)
|
|
: child(child)
|
|
{}
|
|
|
|
shared_ptr<Instance> child;
|
|
};
|
|
class ChildRemovedSignalData : public ICombinedSignalData
|
|
{
|
|
public:
|
|
ChildRemovedSignalData(const shared_ptr<Instance>& child)
|
|
: child(child)
|
|
{}
|
|
|
|
shared_ptr<Instance> child;
|
|
};
|
|
class AncestryChangedSignalData: public ICombinedSignalData
|
|
{
|
|
public:
|
|
AncestryChangedSignalData(const shared_ptr<Instance>& child, const shared_ptr<Instance>& newParent)
|
|
: child(child)
|
|
, newParent(newParent)
|
|
{}
|
|
|
|
shared_ptr<Instance> child;
|
|
shared_ptr<Instance> newParent;
|
|
};
|
|
class OutfitChangedSignalData: public ICombinedSignalData
|
|
{
|
|
public:
|
|
OutfitChangedSignalData()
|
|
{}
|
|
};
|
|
|
|
class PropertyChangedSignalData : public ICombinedSignalData
|
|
{
|
|
public:
|
|
PropertyChangedSignalData(const Reflection::PropertyDescriptor* propertyDescriptor)
|
|
:propertyDescriptor(propertyDescriptor)
|
|
{}
|
|
const Reflection::PropertyDescriptor* propertyDescriptor;
|
|
};
|
|
|
|
class EventInvocationSignalData : public ICombinedSignalData
|
|
{
|
|
public:
|
|
EventInvocationSignalData(const Reflection::EventDescriptor* eventDescriptor, const Reflection::EventArguments* eventArguments, const SystemAddress* target)
|
|
:eventDescriptor(eventDescriptor)
|
|
,eventArguments(eventArguments)
|
|
,target(target)
|
|
{}
|
|
const Reflection::EventDescriptor* eventDescriptor;
|
|
const Reflection::EventArguments* eventArguments;
|
|
const SystemAddress* target;
|
|
};
|
|
|
|
class HumanoidChangedSignalData: public ICombinedSignalData
|
|
{
|
|
public:
|
|
HumanoidChangedSignalData()
|
|
{}
|
|
};
|
|
|
|
virtual void humanoidChanged();
|
|
|
|
|
|
void childAddedSignal(shared_ptr<Instance>& inst) { if(onDemandRead()) onDemandWrite()->childAddedSignal(inst); }
|
|
rbx::signal<void(shared_ptr<Instance>)>* getOrCreateChildAddedSignal(bool create = true) { return (onDemandRead() || create) ? &onDemandWrite()->childAddedSignal : NULL; };
|
|
|
|
void childRemovedSignal(shared_ptr<Instance>& inst) { if(onDemandRead()) onDemandWrite()->childRemovedSignal(inst); }
|
|
rbx::signal<void(shared_ptr<Instance>)>* getOrCreateChildRemovedSignal(bool create = true) { return (onDemandRead() || create) ? &onDemandWrite()->childRemovedSignal : NULL; };
|
|
|
|
void descendantAddedSignal(shared_ptr<Instance>& inst) { if(onDemandRead()) onDemandWrite()->descendantAddedSignal(inst); }
|
|
rbx::signal<void(shared_ptr<Instance>)>* getOrCreateDescendantAddedSignal(bool create = true) { return (onDemandRead() || create) ? &onDemandWrite()->descendantAddedSignal : NULL; };
|
|
|
|
void descendantRemovingSignal(const shared_ptr<Instance>& inst) { if(onDemandRead()) onDemandWrite()->descendantRemovingSignal(inst); }
|
|
rbx::signal<void(shared_ptr<Instance>)>* getOrCreateDescendantRemovingSignal(bool create = true) { return (onDemandRead() || create) ? &onDemandWrite()->descendantRemovingSignal : NULL; };
|
|
|
|
rbx::signal<void(shared_ptr<Instance>, shared_ptr<Instance>)> ancestryChangedSignal;
|
|
rbx::signal<void(const Reflection::PropertyDescriptor*)> propertyChangedSignal;
|
|
|
|
// combinedSignal is an optimization. You could use the regular signals, like childAddedSignal.
|
|
// However, if you are listening to many signals then you can save memory by listening to just the combinedSignal
|
|
rbx::signal<void(CombinedSignalType, const ICombinedSignalData*)> combinedSignal;
|
|
|
|
shared_ptr<Instance> clone(CreatorRole creatorRole);
|
|
virtual shared_ptr<Instance> luaClone(); //Just like regular clone, but it enforces the Instance limits
|
|
static XmlElement* toNewXmlRoot(Instance* instance, RBX::CreatorRole creatorRole); // DB 12/5/05 - added here to centralize the spawner behavior - to, from XML
|
|
|
|
void removeAllChildren();
|
|
|
|
// security: this call has checks that prevent it from being called from most code.
|
|
void destroyAllChildrenLua();
|
|
|
|
const std::string& getClassNameStr() const { return getClassName().toString(); } // used by reflection
|
|
|
|
std::string getReadableDebugId(int scopeLength) const { return getGuid().readableString(scopeLength); }
|
|
std::string getReadableDebugId() const { return getGuid().readableString(); }
|
|
std::string getReadableDebugId(int scopeLength) { return getGuid().readableString(scopeLength); }
|
|
|
|
inline Instance* getParent() {return parent;}
|
|
inline const Instance* getParent() const {return parent;}
|
|
private:
|
|
inline Instance* getParentDangerous() const {return parent;} // only used by refProp descriptor
|
|
|
|
public:
|
|
inline Instance* getRootAncestor() {return parent ? parent->getRootAncestor() : this;}
|
|
inline const Instance* getRootAncestor() const {return parent ? parent->getRootAncestor() : this;}
|
|
|
|
static Instance* getRootAncestor(Instance* instance) { return instance ? instance->getRootAncestor() : NULL; }
|
|
static const Instance* getRootAncestor(const Instance* instance) { return instance ? instance->getRootAncestor() : NULL; }
|
|
|
|
bool getRobloxLocked() const { return robloxLocked; }
|
|
void setRobloxLocked(bool value);
|
|
|
|
bool contains(const Instance* child) const; // Recursive. Also returns true if this==child
|
|
|
|
void setParent(Instance* instance) { setParentInternal(instance, false); }
|
|
bool setLockedParent(Instance* instance) { return setParentInternal(instance, true); }
|
|
void setParent2(shared_ptr<Instance> instance) {
|
|
setParent(instance.get());
|
|
}
|
|
void promoteChildren();
|
|
static const void* getSetParentAddr();
|
|
|
|
// locks the parent and then sets it
|
|
// if this fails to set the parent it will unlock the parent before returning
|
|
// if the parent is successfully set the parent will stay locked
|
|
void setAndLockParent(Instance* instance);
|
|
|
|
const std::string& getName() const {return name.get(); }
|
|
virtual void setName(const std::string& value);
|
|
|
|
std::string getFullName() const; // Render up to (but not including) the root node
|
|
std::string getFullNameForReflection() { return getFullName(); }
|
|
|
|
bool isAncestorOf(const Instance* descendant) const
|
|
{
|
|
if (descendant==NULL)
|
|
return false;
|
|
else if (descendant->getParent()==this)
|
|
return true;
|
|
else
|
|
return isAncestorOf(descendant->getParent());
|
|
}
|
|
|
|
bool isAncestorOf2(shared_ptr<Instance> descendant) { return isAncestorOf(descendant.get()); }
|
|
bool isDescendantOf2(shared_ptr<Instance> ancestor) { return isDescendantOf(ancestor.get()); }
|
|
|
|
// isDescendantOf will return true if parent==NULL (consistent with the fact that a top-level Instance has parent==NULL)
|
|
bool isDescendantOf(const Instance* ancestor) const
|
|
{
|
|
const Instance* parent = getParent();
|
|
if (ancestor==parent)
|
|
return true;
|
|
else if (parent!=NULL)
|
|
return parent->isDescendantOf(ancestor);
|
|
else
|
|
return false;
|
|
}
|
|
|
|
template<class Type>
|
|
Type* findFirstAncestorOfType()
|
|
{
|
|
Instance* parent = getParent();
|
|
if (Type* parentType = this->fastDynamicCast<Type>(parent))
|
|
return parentType;
|
|
else if (parent!=NULL)
|
|
return parent->findFirstAncestorOfType<Type>();
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
size_t numChildren() const {return children ? children->size() : 0;}
|
|
|
|
int findChildIndex(const Instance* instance) const;
|
|
virtual int getPersistentDataCost() const ;
|
|
static int computeStringCost(const std::string& value)
|
|
{
|
|
return std::max<int>(1, value.length() / 100);
|
|
}
|
|
|
|
|
|
Instance* getChild(size_t index) {return (*children)[index].get();}
|
|
const Instance* getChild(size_t index) const {return (*children)[index].get();}
|
|
|
|
void waitForChild(std::string childName, boost::function<void(shared_ptr<Instance>)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
|
void checkParentWaitingForChildren();
|
|
|
|
// Find the first ancestor of a given descendant
|
|
shared_ptr<Instance> findFirstAncestorOf(const Instance* descendant) const;
|
|
|
|
// TODO: findFirstChildByName should return const Instance*
|
|
|
|
const Instance* findConstFirstChildByName(const std::string& findName) const;
|
|
Instance* findFirstChildByName(const std::string& findName) {
|
|
return const_cast<Instance*>(findConstFirstChildByName(findName));
|
|
}
|
|
Instance* findFirstChildByNameDangerous(const std::string& findName) const { // used by reflection - breaking from const to non const
|
|
return const_cast<Instance*>(findConstFirstChildByName(findName));
|
|
}
|
|
|
|
// breadth-first search
|
|
Instance* findFirstChildByNameRecursive(const std::string& findName);
|
|
|
|
// Used for Reflection:
|
|
shared_ptr<Instance> findFirstChildByName2(std::string findName, bool recursive) {
|
|
return recursive ? shared_from(findFirstChildByNameRecursive(findName)) : shared_from(findFirstChildByName(findName));
|
|
}
|
|
|
|
// queryTypedChild
|
|
template<class Type>
|
|
Type* queryTypedChild(int index) {
|
|
return dynamic_cast<Type*>((*children)[index].get());
|
|
}
|
|
template<class Type>
|
|
const Type* queryTypedChild(int index) const {
|
|
return dynamic_cast<const Type*>((*children)[index].get());
|
|
}
|
|
|
|
// getTypedChild
|
|
template<class Type>
|
|
Type* getTypedChild(int index) {
|
|
return rbx_static_cast<Type*>((*children)[index].get());
|
|
}
|
|
|
|
template<class Type>
|
|
const Type* getTypedChild(int index) const {
|
|
return rbx_static_cast<const Type*>((*children)[index].get());
|
|
}
|
|
|
|
// queryTypedParent
|
|
template<class Type>
|
|
Type* queryTypedParent() {
|
|
return dynamic_cast<Type*>(parent);
|
|
}
|
|
|
|
template<class Type>
|
|
const Type* queryTypedParent() const {
|
|
return dynamic_cast<const Type*>(parent);
|
|
}
|
|
|
|
// getTypedParent
|
|
template<class Type>
|
|
Type* getTypedParent() {
|
|
return rbx_static_cast<Type*>(parent);
|
|
}
|
|
|
|
template<class Type>
|
|
const Type* getTypedParent() const {
|
|
return rbx_static_cast<const Type*>(parent);
|
|
}
|
|
|
|
// getTypedRoot
|
|
template<class Type>
|
|
Type* getTypedRoot() {
|
|
RBXASSERT(dynamic_cast<Type*>(this));
|
|
if (Type* typedParent = dynamic_cast<Type*>(parent)) {
|
|
return typedParent->template getTypedRoot<Type>();
|
|
}
|
|
else {
|
|
return static_cast<Type*>(this);
|
|
}
|
|
}
|
|
template<class Type>
|
|
const Type* getTypedRoot() const {
|
|
RBXASSERT(dynamic_cast<const Type*>(this));
|
|
if (const Type* typedParent = dynamic_cast<const Type*>(parent)) {
|
|
return typedParent->template getTypedRoot<Type>();
|
|
}
|
|
else {
|
|
return static_cast<const Type*>(this);
|
|
}
|
|
}
|
|
|
|
const copy_on_write_ptr<Instances>& getChildren() const { return children; }
|
|
|
|
// Used for reflection. Note that it might return NULL (or an empty container)
|
|
// TODO - this is dangerous? getting const children?
|
|
shared_ptr<const Instances> getChildren2() { return children.read(); }
|
|
|
|
template<class Func>
|
|
inline void visitChildren(const Func& func) const {
|
|
if (children)
|
|
{
|
|
boost::shared_ptr<const Instances> c(children.read());
|
|
Instances::const_iterator end = c->end();
|
|
for (Instances::const_iterator iter = c->begin(); iter!=end; ++iter)
|
|
{
|
|
const_cast<Func&>(func)(*iter);
|
|
}
|
|
}
|
|
}
|
|
|
|
template<class Type>
|
|
inline int countDescendantsOfType() const {
|
|
int total = fastDynamicCast<Type>() ? 1 : 0;
|
|
if (children)
|
|
{
|
|
boost::shared_ptr<const Instances> c(children.read());
|
|
Instances::const_iterator end = c->end();
|
|
for (Instances::const_iterator iter = c->begin(); iter!=end; ++iter)
|
|
{
|
|
total += (*iter)->countDescendantsOfType<Type>();
|
|
}
|
|
}
|
|
return total;
|
|
}
|
|
|
|
template<class Func>
|
|
inline void visitDescendants(const Func& func) const {
|
|
if (children)
|
|
{
|
|
boost::shared_ptr<const Instances> c(children.read());
|
|
Instances::const_iterator end = c->end();
|
|
for (Instances::const_iterator iter = c->begin(); iter!=end; ++iter)
|
|
{
|
|
const_cast<Func&>(func)(*iter);
|
|
(*iter)->visitDescendants(func);
|
|
}
|
|
}
|
|
}
|
|
|
|
template<class C>
|
|
const C* findConstFirstChildOfType() const
|
|
{
|
|
if (children)
|
|
{
|
|
Instances::const_iterator end = children->end();
|
|
for (Instances::const_iterator iter = children->begin(); iter!=end; ++iter) {
|
|
const C* c = fastDynamicCast<C>(iter->get());
|
|
if (c!=NULL)
|
|
return c;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
template<class C>
|
|
C* findFirstChildOfType()
|
|
{
|
|
return const_cast<C*>(findConstFirstChildOfType<C>());
|
|
}
|
|
|
|
Instance* findFirstChildOfType(const std::string& className)
|
|
{
|
|
if (children)
|
|
{
|
|
Instances::const_iterator end = children->end();
|
|
for (Instances::const_iterator iter = children->begin(); iter!=end; ++iter) {
|
|
if(iter->get()->getClassNameStr() == className)
|
|
return iter->get();
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
template<class C>
|
|
C* findFirstDescendantOfType()
|
|
{
|
|
if (children)
|
|
{
|
|
Instances::const_iterator end = children->end();
|
|
for (Instances::const_iterator iter = children->begin(); iter!=end; ++iter) {
|
|
Instance* i = iter->get();
|
|
if (C* foundChild = fastDynamicCast<C>(i)) {
|
|
return foundChild;
|
|
}
|
|
if (C* foundDesc = i->findFirstDescendantOfType<C>()) {
|
|
return foundDesc;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
template<class C>
|
|
const C* findConstFirstDescendantOfType() const
|
|
{
|
|
if (children)
|
|
{
|
|
Instances::const_iterator end = children->end();
|
|
for (Instances::const_iterator iter = children->begin(); iter!=end; ++iter) {
|
|
const Instance* i = iter->get();
|
|
if (const C* foundChild = fastDynamicCast<C>(i)) {
|
|
return foundChild;
|
|
}
|
|
if (const C* foundDesc = i->findConstFirstDescendantOfType<C>()) {
|
|
return foundDesc;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
template<class C>
|
|
void destroyDescendantsOfType()
|
|
{
|
|
if (children)
|
|
{
|
|
Instances::const_iterator end = children->end();
|
|
for (Instances::const_iterator iter = children->begin(); iter!=end; ++iter) {
|
|
if (const C* c = fastDynamicCast<C>(iter->get()))
|
|
{
|
|
iter->get()->destroy();
|
|
} else {
|
|
iter->get()->destroyDescendantsOfType<C>();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static Instance* findCommonNode(Instance* i1, Instance* i2)
|
|
{
|
|
if (i1 == i2)
|
|
return i1;
|
|
|
|
if (!i1)
|
|
return NULL;
|
|
if (i1->isAncestorOf(i2))
|
|
return i1;
|
|
|
|
if (!i2)
|
|
return NULL;
|
|
if (i2->isAncestorOf(i1))
|
|
return i2;
|
|
|
|
return findCommonNode(i1->getParent(), i2->getParent());
|
|
}
|
|
|
|
// Override with class-specific rules about what can be a child or parent
|
|
bool canAddChild(const Instance* instance, bool checkParent = true) const {
|
|
if (instance->contains(this))
|
|
return false; // No circular ownership, please!
|
|
if (checkParent && instance->getParent() == this)
|
|
return false; // Already a child!
|
|
if (this->askForbidChild(instance))
|
|
return false;
|
|
if (instance->askForbidParent(this))
|
|
return false;
|
|
if (this->askAddChild(instance))
|
|
return true;
|
|
if (instance->askSetParent(this))
|
|
return true;
|
|
return false;
|
|
}
|
|
bool canAddChild(const shared_ptr<Instance>& instance) const {
|
|
return (instance!=NULL) && canAddChild(instance.get());
|
|
}
|
|
bool canSetParent(const Instance* instance) const {
|
|
return (instance==NULL) || instance->canAddChild(this);
|
|
}
|
|
|
|
template<typename Iter>
|
|
bool canSetChildren(Iter first, Iter last) const
|
|
{
|
|
for (; first != last; ++first)
|
|
{
|
|
if (!canAddChild(*first))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template<typename Iter>
|
|
void setChildren(Iter first, Iter last)
|
|
{
|
|
for (; first != last; ++first)
|
|
{
|
|
Instance* instance = *first;
|
|
RBXASSERT(canAddChild(instance));
|
|
instance->setParent(this);
|
|
}
|
|
}
|
|
|
|
virtual bool canClientCreate() { return false; }
|
|
// This convenience function gets called when this Instances is added to or removed from the DataModel.
|
|
// You can use it to find Services, connect to and disconnect from Notifiers, etc.
|
|
virtual void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider) {}
|
|
|
|
void readProperties(const XmlElement* container, IReferenceBinder& binder);
|
|
|
|
virtual shared_ptr<Instance> createChild(const RBX::Name& className, RBX::CreatorRole creatorRole);
|
|
void read(const XmlElement* element, IReferenceBinder& binder, RBX::CreatorRole creatorRole);
|
|
void readChildren(const XmlElement* element, IReferenceBinder& binder, RBX::CreatorRole creatorRole);
|
|
void readChild(const XmlElement* childElement, IReferenceBinder& binder, RBX::CreatorRole creatorRole);
|
|
|
|
virtual XmlElement* writeXml(const boost::function<bool(Instance*)>& isInScope, RBX::CreatorRole creatorRole);
|
|
|
|
void writeChildren(XmlElement* container, const boost::function<bool(Instance*)>& isInScope, RBX::CreatorRole creatorRole, const SaveFilter saveFilter = SAVE_ALL);
|
|
void writeChildren(XmlElement* container, RBX::CreatorRole creatorRole, const SaveFilter saveFilter = SAVE_ALL);
|
|
|
|
void raisePropertyChanged(const RBX::Reflection::PropertyDescriptor& descriptor);
|
|
void raiseEventInvocation(const RBX::Reflection::EventDescriptor& descriptor, const RBX::Reflection::EventArguments& args, const SystemAddress* target);
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
protected:
|
|
//Actively prevent the adding
|
|
virtual void verifySetParent(const Instance* newParent) const {};
|
|
virtual void verifySetAncestor(const Instance* const newParent, const Instance* const instanceGettingNewParent) const;
|
|
//Actively prevent the adding (parent side)
|
|
virtual void verifyAddChild(const Instance* newChild) const {};
|
|
virtual void verifyAddDescendant(const Instance* newParent, const Instance* instanceGettingNewParent) const;
|
|
|
|
// Don't call this directly. Call canAddChild
|
|
virtual bool askAddChild(const Instance* instance) const;
|
|
// Don't call this directly. Call canAddChild
|
|
virtual bool askForbidChild(const Instance* instance) const;
|
|
|
|
// Don't call this directly. Call canSetParent
|
|
virtual bool askForbidParent(const Instance* instance) const;
|
|
// Don't call this directly. Call canSetParent
|
|
virtual bool askSetParent(const Instance* instance) const;
|
|
|
|
|
|
virtual void onAncestorChanged(const AncestorChanged& event);
|
|
|
|
virtual void onDescendantAdded(Instance* instance);
|
|
virtual void onDescendantRemoving(const shared_ptr<Instance>& instance);
|
|
|
|
virtual void onChildAdded(Instance* child) {}
|
|
virtual void onChildRemoving(Instance* child) {}
|
|
virtual void onChildRemoved(Instance* child) {}
|
|
virtual void onChildChanged(Instance* instance, const PropertyChanged& event);
|
|
|
|
virtual void onPropertyChanged(const Reflection::PropertyDescriptor& descriptor) {}
|
|
|
|
|
|
|
|
virtual void readProperty(const XmlElement* propertyElement, IReferenceBinder& binder);
|
|
|
|
void raiseChanged(const RBX::Reflection::PropertyDescriptor& descriptor)
|
|
{
|
|
raisePropertyChanged(descriptor);
|
|
}
|
|
|
|
private:
|
|
static void signalDescendantAdded(Instance* instance, Instance* beginParent, Instance* endParent);
|
|
static void signalDescendantRemoving(const shared_ptr<Instance>& instance, Instance* beginParent, Instance* endParent);
|
|
|
|
void writeProperties(XmlElement* container) const;
|
|
bool setParentInternal(Instance* instance, bool ignoreLock);
|
|
};
|
|
|
|
} // namespace RBX
|