This commit is contained in:
watrabi
2025-10-28 14:05:46 -04:00
parent 977f1ff4b8
commit c93494f795
452 changed files with 47860 additions and 152 deletions
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#include <string>
#include <iostream>
#include <vector>
#include <map>
#include "reflection/enumconverter.h"
#include "V8Tree/Property.h"
namespace RBX {
}
+778
View File
@@ -0,0 +1,778 @@
/* 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
+46
View File
@@ -0,0 +1,46 @@
#pragma once
#include "Reflection/Property.h"
#include "V8Xml/Reference.h"
#include "V8Xml/XmlElement.h"
#include "Util/Object.h"
#include "G3D/Vector3.h"
#include "G3D/Color3.h"
#include <string>
#include <iostream>
#include <vector>
namespace RBX {
class PropertyChanged
{
RBX::Reflection::Property property;
public:
const RBX::Reflection::Property& getProperty() const { return property; }
const RBX::Reflection::PropertyDescriptor& getDescriptor() const { return property.getDescriptor(); }
const RBX::Name& getName() { return property.getName(); }
PropertyChanged(const PropertyChanged& other)
: property(other.property)
{}
private:
friend class Instance;
PropertyChanged(const RBX::Reflection::Property& p)
: property(p)
{}
};
// TODO: Move this out of Reflection?
// Some standard categories
#define category_Data "Data"
#define category_Behavior "Behavior"
#define category_State "State"
#define category_Appearance "Appearance"
#define category_Team "Team"
#define category_Image "Image"
#define category_Video "Video"
#define category_Control "Control"
}
+254
View File
@@ -0,0 +1,254 @@
#pragma once
#include "V8Tree/Instance.h"
#include <boost/type_traits/is_base_of.hpp>
#include <boost/static_assert.hpp>
namespace RBX
{
// A Service is an instance that is "Singleton" in the scope of its containing
// ServiceProvider. In other words, a Service-derived class is unique within the
// child tree of a ServiceProvider.
class Service
{
public:
const bool isPublic;
protected:
Service(bool isPublic=true)
:isPublic(isPublic)
{
}
~Service()
{
}
};
// Design decision: ServiceProvider liberally uses mutable members and declares const
// member functions that change data. The idea here is that a ServiceProvider doesn't really
// change fundamentally when creating and providing Services. It lets clients get a service
// when const
// TODO: consider the relative merits of returning Services as shared_ptr: safer, but more likely to lead to ptr leaks?
extern const char* const sServiceProvider;
class ServiceProvider
: public DescribedNonCreatable<ServiceProvider, Instance, sServiceProvider>
{
private:
typedef DescribedNonCreatable<ServiceProvider, Instance, sServiceProvider> Super;
static Reflection::BoundFuncDesc<ServiceProvider, shared_ptr<Instance>(std::string)> func_FindService;
static Reflection::BoundFuncDesc<ServiceProvider, shared_ptr<Instance>(std::string)> func_GetService;
static Reflection::BoundFuncDesc<ServiceProvider, shared_ptr<Instance>(std::string)> dep_service;
static Reflection::BoundFuncDesc<ServiceProvider, shared_ptr<Instance>(std::string)> dep_GetService;
typedef std::vector< shared_ptr<Instance> > ServiceArray;
mutable ServiceArray serviceArray;
mutable std::map<const RBX::Name*, shared_ptr<Instance> > serviceMap;
public:
rbx::signal<void()> closingSignal;
rbx::signal<void()> closingLateSignal;
rbx::signal<void(shared_ptr<Instance> service)> serviceAddedSignal;
rbx::signal<void(shared_ptr<Instance> service)> serviceRemovingSignal;
ServiceProvider();
ServiceProvider(const char* name);
template<class ServiceClass>
ServiceClass* find() const
{
BOOST_STATIC_ASSERT((boost::is_base_of<Service, ServiceClass>::value));
BOOST_STATIC_ASSERT((boost::is_base_of<Instance, ServiceClass>::value));
size_t index = getClassIndex<ServiceClass>();
ServiceClass* service;
if (index+1>serviceArray.size())
{
// serviceArray has an empty entry for this service now
serviceArray.resize(index+1, shared_ptr<Instance>());
service = NULL;
}
else
{
service = static_cast<ServiceClass*>(serviceArray[index].get());
if (service!=NULL)
return service;
}
if (ServiceClass::isNullClassName())
return NULL;
// See if the service is in the className map
shared_ptr<Instance> i = findServiceByClassName(ServiceClass::className());
if (!i)
return NULL;
service = boost::polymorphic_downcast<ServiceClass*>(i.get());
serviceArray[index] = i;
return service;
}
//mutable RBX::reentrant_concurrency_catcher threadGuard;
template<class ServiceClass>
ServiceClass* create() const
{
//RBX::reentrant_concurrency_catcher::scoped_lock lock(threadGuard);
ServiceClass* service = this->find<ServiceClass>();
if (service==NULL)
{
// If all else fails, create the service and put it in the table and map
shared_ptr<ServiceClass> s = Creatable<Instance>::create<ServiceClass>();
service = s.get();
// setParent can throw, so do not mutate any internal state until
// setParent has returned without throwing.
service->setAndLockParent(const_cast<ServiceProvider*>(this));
size_t index = getClassIndex<ServiceClass>();
serviceArray[index] = s;
// By now onDescendantAdded will have been called, which will add the service to serviceMap:
RBXASSERT((ServiceClass::className()==RBX::Name::getNullName()) || serviceMap.find(&ServiceClass::className())!=serviceMap.end());
}
return service;
}
static const ServiceProvider* findServiceProvider(const Instance* context)
{
if (const Instance* root = Instance::getRootAncestor(context)) {
if (const ServiceProvider* serviceProvider = Instance::fastDynamicCast<ServiceProvider>(root)) {
return serviceProvider;
}
}
return NULL;
}
template<class ServiceClass>
static ServiceClass* find(const Instance* context)
{
if (const ServiceProvider* serviceProvider = findServiceProvider(context))
return serviceProvider->find<ServiceClass>();
return NULL;
}
template<class ServiceClass>
static ServiceClass* find(const ServiceProvider* serviceProvider)
{
if (serviceProvider!=NULL)
return serviceProvider->find<ServiceClass>();
return NULL;
}
template<class ServiceClass>
static ServiceClass* create(const Instance* context)
{
if (const ServiceProvider* serviceProvider = findServiceProvider(context))
return serviceProvider->create<ServiceClass>();
return NULL;
}
template<class ServiceClass>
static ServiceClass* create(const ServiceProvider* serviceProvider)
{
if (serviceProvider!=NULL)
return serviceProvider->create<ServiceClass>();
return NULL;
}
// Less efficient factory functions that use classNames. Use only if
// you don't know the type of Service you want.
static shared_ptr<Instance> create(Instance* context, const RBX::Name& name);
shared_ptr<Instance> getPublicServiceByClassNameString(std::string name);
protected:
/*override*/ shared_ptr<Instance> createChild(const RBX::Name& className, RBX::CreatorRole creatorRole);
/*override*/ void onDescendantRemoving(const shared_ptr<Instance>& instance);
/*override*/ void onDescendantAdded(Instance* instance);
/*override*/ void onChildAdded(Instance* child);
/*override*/ void onChildRemoving(Instance* child);
/* override */ bool askAddChild(const Instance* instance) const
{
return dynamic_cast<const Service*>(instance) != NULL;
}
void clearServices();
private:
// Returns a new number each time it is called (starting at 0)
static size_t newIndex();
template<class ServiceClass>
static size_t doGetClassIndex()
{
static size_t index = newIndex();
return index;
}
template<class ServiceClass>
static void callDoGetClassIndex()
{
doGetClassIndex<ServiceClass>();
}
shared_ptr<Instance> findServiceByClassName(const RBX::Name& className) const;
shared_ptr<Instance> findPublicServiceByClassNameString(std::string name);
private:
template<class ServiceClass>
static size_t getClassIndex()
{
static boost::once_flag flag = BOOST_ONCE_INIT;
boost::call_once(&callDoGetClassIndex<ServiceClass>, flag);
return doGetClassIndex<ServiceClass>();
}
};
// A convenience class. Given an Instance* context, it will find the Service. You can treat it
// like a Service*. Please note that it might be NULL if the context isn't a child of a ServiceProvider (like DataModel)
template<class S>
class ServiceClient
{
Instance* context;
mutable shared_ptr<S> service;
public:
ServiceClient(Instance* context)
:context(context)
{
}
bool isNull() const
{
return findService()==NULL;
}
operator S*()
{
return createService();
}
operator const S*() const
{
return createService();
}
const S* operator->() const
{
return createService();
}
S* operator->()
{
return createService();
}
private:
S* findService() const
{
if (!service)
service = shared_from(ServiceProvider::find<S>(context));
return service.get();
}
S* createService() const
{
if (!service)
service = shared_from(ServiceProvider::create<S>(context));
return service.get();
}
};
}
+164
View File
@@ -0,0 +1,164 @@
/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */
#pragma once
#include <map>
#include <boost/function.hpp>
#include "rbx/Debug.h"
#include "security/FuzzyTokens.h"
#include "v8datamodel/HackDefines.h"
LOGGROUP(Verbs)
namespace RBX {
class Name;
class VerbContainer;
class RBXInterface IDataState
{
public:
// Sets the document's "dirty" bit
virtual void setDirty(bool dirty) = 0;
virtual bool isDirty() const = 0;
};
class RBXBaseClass Verb
{
const Name& name;
protected:
VerbContainer* const container;
Verb(VerbContainer* container, const std::string& name, bool blacklisted = false);
Verb(VerbContainer* container, const Name& name, bool blacklisted = false);
bool verbSecurity;
public:
virtual ~Verb();
virtual bool isEnabled() const { return true; }
virtual bool isChecked() const { return false; }
virtual bool isSelected() const { return false; }
virtual std::string getText() const { return std::string(); }
const Name& getName() const {return name;}
virtual void doIt(IDataState* dataState) = 0;
VerbContainer* getContainer() const { return container; }
bool getVerbSecurity() const { return verbSecurity; }
static inline void doItWithChecks(Verb* const verb, IDataState* dataState)
{
#if !defined(RBX_STUDIO_BUILD)
if (verb->getVerbSecurity())
{
RBX::Security::setHackFlagVs<LINE_RAND1>(RBX::Security::hackFlag9, HATE_VERB_SNATCH);
}
else
#endif
{
verb->doIt(dataState);
}
}
};
// This version of Verb lets you pass in callbacks for the implementation
class BoundVerb : public Verb
{
boost::function<void(VerbContainer*)> doItFunction;
boost::function<bool(VerbContainer*)> isEnabledFunction;
boost::function<bool(VerbContainer*)> isCheckedFunction;
boost::function<std::string(VerbContainer*)> getTextFunction;
public:
BoundVerb(VerbContainer* container, const char* name,
boost::function<void(VerbContainer*)> doItFunction,
boost::function<bool(VerbContainer*)> isEnabledFunction = NULL,
boost::function<bool(VerbContainer*)> isCheckedFunction = NULL,
boost::function<std::string(VerbContainer*)> getTextFunction = NULL)
: Verb(container, name)
, doItFunction(doItFunction)
, isEnabledFunction(isEnabledFunction)
, isCheckedFunction(isCheckedFunction)
, getTextFunction(getTextFunction)
{
}
virtual bool isEnabled() const
{
if (!isEnabledFunction)
return Verb::isEnabled();
return isEnabledFunction(container);
}
virtual bool isChecked() const
{
if (!isCheckedFunction)
return Verb::isChecked();
return isCheckedFunction(container);
}
virtual std::string getText() const
{
if (!getTextFunction)
return Verb::getText();
return getTextFunction(container);
}
virtual void doIt(IDataState* dataState)
{
if (doItFunction)
doItFunction(container);
}
};
class NullVerb : public Verb
{
public:
NullVerb(VerbContainer* container, const std::string& name)
: Verb(container, name)
{
}
/*override*/ bool isEnabled() const { return false; }
/*override*/ void doIt(IDataState* dataState) {}
};
class VerbContainer
{
friend class Verb;
typedef std::map<const Name*, Verb*> Verbs;
Verbs whitelistVerbs; // used for UI verbs
Verbs blacklistVerbs; // used for tools
VerbContainer* parent;
void addWhitelistVerb(Verb* verb);
void addBlacklistVerb(Verb* verb);
void removeVerb(Verb* verb);
public:
VerbContainer(VerbContainer* parent);
virtual ~VerbContainer();
Verb* getVerb(const Name& name);
Verb* getVerb(const std::string& name);
Verb* getWhitelistVerb(const Name& name);
Verb* getWhitelistVerb(const std::string& name);
// This version of getVerb is used as a minor obsfucation of a string.
Verb* getWhitelistVerb(const std::string& prefix, const std::string& name, const std::string& suffix);
void setVerbParent(VerbContainer* parent);
VerbContainer* getVerbParent() const { return parent; }
template<class F>
void eachVerbName(F f, bool includeParent = true)
{
for (Verbs::const_iterator iter = whitelistVerbs.begin(); iter != whitelistVerbs.end(); ++iter)
f(iter->first);
for (Verbs::const_iterator iter = blacklistVerbs.begin(); iter != blacklistVerbs.end(); ++iter)
f(iter->first);
if (includeParent && parent)
parent->eachVerbName(f);
}
};
} // namespace RBX