Menu
Lumberyard
Developer Guide (Version 1.11)

CryExtension

The complexity of Lumberyard can be challenging to both newcomers and experienced users who want to understand, configure, run, and extend it. Refactoring Lumberyard into extensions makes it easier to manage. Existing features can be unplugged (at least to some degree), replaced, or customized, and new features added. Extensions can consolidate code for a single feature in one location. This avoids having to implement a feature piecemeal across a number of the engine's base modules. Refactoring into extensions can also make the system more understandable at a high level.

Lumberyard's extension framework is loosely based on some fundamental concepts found in Microsoft's Component Object Model (COM). The framework defines two base interfaces that each extension needs to implement, namely ICryUnknown and ICryFactory. These are similar to COM's IUnknown and IClassFactory. The interfaces serve as a base to instantiate extensions, allow interface type casting, and enable query and exposure functionality.

The framework utilizes the concept of shared pointers and is implemented in a way to enforce their consistent usage to help reduce the chance of resource leaks. A set of C++ templates wrapped in a few macros is provided as Glue Code Macros that encourage engine refactoring into extensions. The glue code efficiently implements all base services and registers extensions within the engine. Additionally, a few helper functions implement type-safe casting of interface pointers, querying the IDs of extension interfaces, and convenient instantiation of extension classes. Hence, repetitive writing of tedious boilerplate code is unnecessary, and the potential for introducing bugs is reduced. An example is provided in the section Using Glue Code. If the provided glue code is not applicable, then you must implement the interfaces and base services manually, as described in the section Without Using Glue Code.

Clients access extensions through a system wide factory registry. The registry allows specific extension classes to be searched by either name or ID, and extensions to be iterated by using an interface ID.

Composites

The framework allows extensions to expose certain internal objects that they aggregate or are composed of. These so called composites are extensions themselves because they inherit from ICryUnknown. Composites allow you to reuse desired properties like type information at runtime for safe casting and loose coupling.

Shared and raw interface pointers

Although the framework was designed and implemented to utilize shared pointers and enforce their usage in order to reduce the possibility of resource leaks, raw interface pointers can still be acquired. Therefore, care needs to be taken to prevent re-wrapping those raw interface pointers in shared pointer objects. If the original shared pointer object is not passed during construction so that its internal reference counter can be referred to, the consistency of reference counting will be broken and crashes can occur. A best practice is to use raw interface pointers only to operate on interfaces temporarily, and not store them for later use.

GUIDs

You must use globally unique identifiers (GUIDs) to uniquely identify extensions and their interfaces. GUIDs are essentially 128-bit numbers generated by an algorithm to ensure they only exist once within a system such as Lumberyard. The use of GUIDs is key to implementing the type-safe casting of extension interfaces, which is particularly important in large scale development projects. To create GUIDs, you can use readily available tools like the Create GUID feature in Visual Studio or the macro below.

GUIDs are defined as follows.

Copy
struct CryGUID { uint64 hipart; uint64 lopart; ... }; typedef CryGUID CryInterfaceID; typedef CryGUID CryClassID;

Declared in the following framework header files:

  • CryCommon/CryExtension/CryGUID.h

  • CryCommon/CryExtension/CryTypeID.h

The following Visual Studio macro can be used to generate GUIDs conveniently within the IDE. The macro writes GUIDs to the current cursor location in the source code editor window. Once added to Macro Explorer, the macro can be bound to a keyboard shortcut or (custom) toolbar.

Copy
Public Module CryGUIDGenModule Sub GenerateCryGUID() Dim newGuid As System.Guid newGuid = System.Guid.NewGuid() Dim guidStr As String guidStr = newGuid.ToString("N") guidStr = guidStr.Insert(16, ", 0x") guidStr = guidStr.Insert(0, "0x") REM guidStr = guidStr + vbNewLine REM guidStr = guidStr + newGuid.ToString("D") DTE.ActiveDocument.Selection.Text = guidStr End Sub End Module

ICryUnknown

ICryUnknown provides the base interface for all extensions. If making it the top of the class hierarchy is not possible or desired (for example, in third party code), you can apply an additional level of indirection to expose the code by using the extension framework. For an example, see If ICryUnknown Cannot Be the Base of the Extension Class.

ICryUnknown is declared as follows.

Copy
struct ICryUnknown { CRYINTERFACE_DECLARE(ICryUnknown, 0x1000000010001000, 0x1000100000000000) virtual ICryFactory* GetFactory() const = 0; protected: virtual void* QueryInterface(const CryInterfaceID& iid) const = 0; virtual void* QueryComposite(const char* name) const = 0; }; typedef boost::shared_ptr<ICryUnknown> ICryUnknownPtr;
  • GetFactory() returns the factory with which the specified extension object was instantiated. Using the provided glue code this function has constant runtime.

  • QueryInterface() returns a void pointer to the requested interface if the extension implements it, or NULL otherwise. This function was deliberately declared as protected to enforce usage of type-safe interface casting semantics. For information on casting semantics, see Interface casting semantics. When the provided glue code is used, this function has a (worst case) run time that is linear in the number of supported interfaces. Due to glue code implementation details, no additional internal function calls are needed. A generic code generator produces a series of instructions that compares interface IDs and returns a properly cast pointer.

  • QueryComposite() returns a void pointer to the queried composite if the extension exposes it; otherwise, NULL. As with QueryInterface(), this function was deliberately declared as protected to enforce type querying. For information on type querying, see Querying composites. The function has a (worst case) run time linear in the number of exposed composites.

  • Unlike in COM, ICryUnknown does not have AddRef() and Release(). Reference counting is implemented in an non-intrusive way by using shared pointers that are returned by the framework when extension classes are instantiated.

Declared in the following framework header file:

  • CryCommon/CryExtension/ICryUnknown.h

ICryFactory

ICryFactory provides the base interface to instantiate extensions. It is declared as follows.

Copy
struct ICryFactory { virtual const char* GetClassName() const = 0; virtual const CryClassID& GetClassID() const = 0; virtual bool ClassSupports(const CryInterfaceID& iid) const = 0; virtual void ClassSupports(const CryInterfaceID*& pIIDs, size_t& numIIDs) const = 0; virtual ICryUnknownPtr CreateClassInstance() const = 0; protected: virtual ~ICryFactory() {} };
  • GetClassName() returns the name of the extension class. This function has constant run time when the provided glue code is used.

  • GetClassID() returns the ID of the extension class. This function has constant run time when the provided glue code is used.

  • ClassSupports(iid) returns true if the interface with the specified ID is supported by the extension class; otherwise, false. This function has a (worst case) run time linear in the number of supported interfaces when the provided glue code is used.

  • ClassSupports(pIIDs, numIIDs) returns the pointer to an internal array of IDs enumerating all of the interfaces that this extension class supports as well as the length of the array. This function has constant run time when the provided glue code is used.

  • CreateClassInstance() dynamically creates an instance of the extension class and returns a shared pointer to it. If the extension class is implemented as a singleton, it will return a (static) shared pointer that wraps the single instance of that extension class. This function has constant run time when the provided glue code is used, except for the cost of the constructor call for non-singleton extensions.

  • The destructor is declared protected to prevent explicit destruction from the client side by using delete, boost::shared_ptr<T>, etc. ICryFactory instances exist (as singletons) throughout the entire lifetime of any Lumberyard process and must not be destroyed.

Declared in the following framework header file:

  • CryCommon/CryExtension/ICryFactory.h

ICryFactoryRegistry

ICryFactoryRegistry is a system-implemented interface that enables clients to query extensions. It is declared as follows.

Copy
struct ICryFactoryRegistry { virtual ICryFactory* GetFactory(const char* cname) const = 0; virtual ICryFactory* GetFactory(const CryClassID& cid) const = 0; virtual void IterateFactories(const CryInterfaceID& iid, ICryFactory** pFactories, size_t& numFactories) const = 0; protected: virtual ~ICryFactoryRegistry() {} };
  • GetFactory(cname) returns the factory of the extension class with the specified name; otherwise, NULL.

  • GetFactory(cid) returns the factory of the extension class with the specified ID; otherwise, NULL.

  • IterateFactory() if pFactories is not NULL, IterateFactory copies up to numFactories entries of pointers to extension factories that support iid. numFactories returns the number of pointers copied. If pFactories is NULL, numFactories returns the total amount of extension factories that support iid.

  • The destructor was declared protected to prevent explicit destruction from the client side by using delete, boost::shared_ptr<T>, etc. ICryFactoryRegistry is a system interface and that exists throughout the entire lifetime of any CryEngine process and must not be destroyed.

Declared in the following framework header file:

  • CryCommon/CryExtension/ICryFactoryRegistry.h

Additional Extensions

Use the methods defined in ICryUnknown for additional functionality.

Interface casting semantics

Interface casting semantics have been implemented to provide syntactically convenient and type-safe casting of interfaces. The syntax was designed to conform with traditional C++ type casts and respects const rules.

Copy
ICryFactory* pFactory = ...; assert(pFactory); ICryUnknownPtr pUnk = pFactory->CreateClassInstance(); IMyExtensionPtr pMyExtension = cryinterface_cast<IMyExtension>(pUnk); if (pMyExtension) { // it's safe to work with pMyExtension }

Interface casting also works on raw interface pointers. However, please consider the guidelines described in the section Shared and raw interface pointers.

Declared in the following framework header file:

  • CryCommon/CryExtension/ICryUnknown.h

Querying interface identifiers

Occasionally, it is necessary to know the ID of an interface, e.g. to pass it to ICryFactoryRegistry::IterateFactories(). This can be done as follows.

Copy
CryInterfaceID iid = cryiidof<IMyExtension>();

Declared in the following framework header file:

  • CryCommon/CryExtension/ICryUnknown.h

Checking pointers

Use this extension to check whether pointers to different interfaces belong to the same class instance.

Copy
IMyExtensionAPtr pA = ...; IMyExtensionBPtr pB = ...; if (CryIsSameClassInstance(pA, pB)) { ... }

This works on both shared and raw interface pointers.

Declared in the following framework header file:

  • CryCommon/CryExtension/ICryUnknown.h

Querying composites

Extensions can be queried for composites as follows.

Copy
IMyExtensionPtr pMyExtension = ...; ICryUnknownPtr pCompUnk = crycomposite_query(pMyExtension, "foo"); IFooPtr pComposite = cryinterface_cast<IFoo>(pCompUnk); if (pComposite) { // it's safe to work with pComposite, a composite of pMyExtention exposed as "foo" implementing IFoo }

A call to crycomposite_query() might return NULL if the specified composite has not yet been created. To gather more information, the query can be rewritten as follows.

Copy
IMyExtensionPtr pMyExtension = ...; bool exposed = false; ICryUnknownPtr pCompUnk = crycomposite_query(pMyExtension, "foo", &exposed); if (exposed) { if (pCompUnk) { // "foo" exposed and created IFooPtr pComposite = cryinterface_cast<IFoo>(pCompUnk); if (pComposite) { // it's safe to work with pComposite, a composite of pMyExtention exposed as "foo" implementing IFoo } } else { // "foo" exposed but not yet created } } else { // "foo" not exposed by pMyExtension }

As with interface casting composite, queries work on raw interface pointers. However, please consider the guidelines described in the section Shared and raw interface pointers.

Declared in the following framework header file:

  • CryCommon/CryExtension/ICryUnknown.h

Glue Code Macros

The following macros provide glue code to implement the base interfaces and services to support the framework in a thread-safe manner. You are strongly encouraged to use them when you implement an extension.

For examples of how these macros work together, see Using Glue Code.

Declared in the following framework header files:

  • CryCommon/CryExtension/Impl/ClassWeaver.h

  • CryCommon/CryExtension/CryGUID.h

CRYINTERFACE_DECLARE(iname, iidHigh, iidLow)

Declares an interface and associated ID. Protects the interfaces from accidentally being deleted on client side. That is, it allows destruction only by using boost::shared_ptr<T>. This macro is required once per interface declaration.

Parameters

iname

The (C++) name of the interface as declared.

iidHigh

The higher 64-bit part of the interface ID (GUID).

iidLow

The lower 64-bit part of the interface ID (GUID).

CRYINTERFACE_BEGIN()

Start marker of the interface list inside the extension class implementation. Required once per extension class declaration.

CRYINTERFACE_ADD(iname)

Marker to add interfaces inside the extension class declaration. It has to be declared in between CRYINTERFACE_BEGIN() and any of the CRYINTERFACE_END*() markers. Only declare the interfaces that the class directly inherits. If deriving from an existing extension class or classes, the inherited interfaces get added automatically. If an interface is declared multiple times, duplicates will be removed. It is not necessary to add ICryUnknown.

Warning

Other interfaces that are not declared will not be castable by using cryinterface_cast<T>().

Parameters

iname

The (C++) name of the interface to be added.

CRYINTERFACE_END()

End marker of the interface list inside the extension class declaration. Use this if not inheriting from any already existing extension class. Required once per extension class declaration. Mutually exclusive with any of the other CRYINTERFACE_END*() markers.

CRYINTERFACE_ENDWITHBASE(base)

End marker of the interface list inside the extension class declaration. Use this if inheriting from an already existing extension class. Required once per extension class declaration. Mutually exclusive with any of the other CRYINTERFACE_END*() markers.

Parameters

base

The (C++) name of the extension class from which derived.

CRYINTERFACE_ENDWITHBASE2(base0, base1)

End marker of the interface list inside the extension class declaration. Use this if inheriting from two already existing extension classes. Required once per extension class declaration. Mutually exclusive with any of the other CRYINTERFACE_END*() markers.

Parameters

base0

The (C++) name of the first extension class from which derived.

base1

The (C++) name of the second extension class from which derived.

CRYINTERFACE_ENDWITHBASE3(base0, base1, base2)

End marker of the interface list inside the extension class declaration. Use this if inheriting from three already existing extension classes. Required once per extension class declaration. Mutually exclusive with any of the other CRYINTERFACE_END*() markers.

Parameters

base0

The (C++) name of the first extension class from which derived.

base1

The (C++) name of the second extension class from which derived.

base2

The (C++) name of the 3rd extension class from which derived.

CRYINTERFACE_SIMPLE(iname)

Convenience macro for the following code sequence (probably the most common extension case):

Copy
CRYINTERFACE_BEGIN() CRYINTERFACE_ADD(iname) CRYINTERFACE_END()

Parameters

iname

The (C++) name of the interface to be added.

CRYCOMPOSITE_BEGIN()

Start marker of the list of exposed composites.

CRYCOMPOSITE_ADD(member, membername)

Marker to add a member of the extension class to the list of exposed composites.

Parameters

member

The (C++) name of the extension class member variable to be exposed. It has to be of type boost::shared_ptr<T>, where T inherits from ICryUnknown. This condition is enforced at compile time.

membername

The name (as C-style string) of the composite by which the composite can later be queried at runtime.

CRYCOMPOSITE_END(implclassname)

End marker of the list of exposed composites. Use this if not inheriting from any extension class that also exposes composites. Mutually exclusive with any of the other CRYCOMPOSITE_END*() markers.

Parameters

implclassname

The (C++) name of the extension class to be implemented.

CRYCOMPOSITE_ENDWITHBASE(implclassname, base)

End marker of the list of exposed composites. Use this if inheriting from one extension class that also exposes composites. Queries will first search in the current class and then look into the base class to find a composite that matches the requested name specified in crycomposite_query(). Mutually exclusive with any of the other CRYCOMPOSITE_END*() markers.

Parameters

implclassname

The (C++) name of the extension class to be implemented.

base

The (C++) name of the extension class derived from.

CRYCOMPOSITE_ENDWITHBASE2(implclassname, base0, base1)

End marker of the list of exposed composites. Use this if inheriting from two extension classes that also expose composites. Queries will first search in the current class and then look into the base classes to find a composite matching the requested name specified in crycomposite_query(). Mutually exclusive with any of the other CRYCOMPOSITE_END*() markers.

Parameters

implclassname

The (C++) name of the extension class to be implemented.

base0

The (C++) name of the first extension class from which derived.

base1

The (C++) name of the second extension class which derived.

CRYCOMPOSITE_ENDWITHBASE3(implclassname, base0, base1, base2)

End marker of the list of exposed composites. Use this if inheriting from three extension classes that also expose composites. Queries will first search in the current class and then look into the base classes to find a composite matching the requested name specified in crycomposite_query(). Mutually exclusive with any of the other CRYCOMPOSITE_END*() markers.

Parameters

implclassname

The (C++) name of the extension class to be implemented.

base0

The (C++) name of the first extension class from which derived.

base1

The (C++) name of the second extension class from which derived.

base2

The (C++) name of the third extension class from which derived.

CRYGENERATE_CLASS(implclassname, cname, cidHigh, cidLow)

Generates code to support base interfaces and services for an extension class that can be instantiated an arbitrary number of times. Required once per extension class declaration. Mutually exclusive to CRYGENERATE_SINGLETONCLASS().

Parameters

implclassname

The C++ class name of the extension.

cname

The extension class name with which it is registered in the registry.

cidHigh

The higher 64-bit part of the extension's class ID (GUID) with which it is registered in the registry.

cidLow

The lower 64-bit part of the extension's class ID (GUID) with which it is registered in the registry.

CRYGENERATE_SINGLETONCLASS(implclassname, cname, cidHigh, cidLow)

Generates code to support base interfaces and services for an extension class that can be instantiated only once (singleton). Required once per extension class declaration. Mutually exclusive with CRYGENERATE_CLASS().

Parameters

implclassname

The C++ class name of the extension.

cname

The extension class name with which it is registered in the registry.

cidHigh

The higher 64-bit part of the extension's class ID (GUID) with which it is registered in the registry.

cidLow

The lower 64-bit part of the extension's class ID (GUID) with which it is registered in the registry.

CRYREGISTER_CLASS(implclassname)

Registers the extension class in the system. Required once per extension class at file scope.

Parameters

implclassname

The C++ class name of the extension.

MAKE_CRYGUID(high, low)

Parameters

Constructs a CryGUID. Useful when searching the registry for extensions by class ID.

high

The higher 64-bit part of the GUID.

low

The lower 64-bit part of the GUID.