Nevertheless, the merging of core7 brings quite a few changes to the orxonox framework. Here's a short summary.
By the way, we now have PLUGINS in Orxonox!!!
But let me first introduce the basics...
Static initialization
Our framework uses quite a bit of static initialization, e.g. RegisterClass(MyClass) which is used to register the class in the framework (i.e. creates an Identifier and a Factory). The way we use it, static initialization has a lot of benefits. But in general, static initialization is a bad thing because it happens before main() and the order of initialization is largely undefined (or at least not under the programmer's control).
If we use static initialization, it should have as little side effects as possible. So far, we did not really adhere to this rule in Orxonox. RegisterClass(MyClass) created an Identifier and registered it in IdentifierManager, which requires that IdentifierManager also exists at this point. Even worse than static initialization is static deinitialization. Stuff gets destroyed in (almost) random order and you never know if you survive it.
With core7, static initialization is a lot cleaner. Every static call like RegisterClass(), CreateConsoleCommand(), registerStaticNetworkFunction() etc. will now only create an independent object and put it into a queue (the list of statically initialized instances in the current module). Only when the framework is fully loaded, these instances are added to their managers (IdentifierManager, ConsoleCommandManager, etc.).
And the best thing about this is: It is reversible. Every module keeps track of the list of statically initialized instances and can unload them when they are not needed anymore (e.g. when the game is shutting down). This means that not only initialization, but also deinitialization is now fully controlled by the framework.
Plugins
The logical consequence of the above point is that we now have plugin support in Orxonox. Since every module can be loaded and unloaded in a controlled manner, we can do this at runtime. This means that (almost) every independent module can be loaded/unloaded during the game. As soon as it is loaded, clases, console commands, etc. become available in the game. As soon as it is unloaded, all instances are destroyed and the features of the plugin are not accessible anymore.
How to use a plugin in Orxonox (short version):
- Mark the module as "PLUGIN" in CMakeLists.txt
- Add the plugin to your level
- Watch the plugin being loaded when the level starts
- Watch it being unloaded when the level ends
The framework now allows destroyDelayed() in addition to the already existing destroy(). The problem with destroy() sometimes is that it deletes the instance immediately (as long as no smart/strong pointers are pointing to it). In some situations this is unwanted, e.g. if the object is destroyed as a consequence of a collision. In this case, Orxonox will crash because the object is destroyed while bullet ist still using it.
This led to strange workarounds like PawnManager (which did nothing else than destroying dead pawns in the next tick) or even stranger ShipPartManager (which did the same to ship parts).
destroyDelayed() introduces the same functionality as a framework feature. Instead of immediately destroying the instance, it merely gets marked as 'to be destroyed'. As soon as the current tick finishes, the instance will be actually destroyed (i.e. destroy() is called).
Note that there is a) no guarantee for the order of destruction and b) no guarantee that a destroyed object is actually deleted (because it may be kept alive by a smart/strong pointer).
Strong Pointer
So far we had SmartPtr and WeakPtr in the Orxonox framework. The confusing thing was that WeakPtr is actually also smart (in contrast to a "dumb" plain C pointer). This is the reason why I renamed SmartPtr to StrongPtr. It's still the same class and has still the same functionality, but it's naming is now easier to understand.
The new guideline reads as follows:
There are StrongPtr and WeakPtr. StrongPtr keeps the object alive, WeakPtr doesn't. Both are Smart-Pointers in the sense that they will either point to an existing object or to NULL, but never ever to an invalid address ("dangling pointer").
Runtime checks
There are some new runtime checks. One part of the checks tries to validate the class hierarchy, i.e. tries to find missing calls to RegisterClass, RegisterObject or situations where the manual definition of the inheritance of an abstract class is wrong.
Another check tries to find situations when collision shapes are destroyed in a collision callback. This will often lead to crashes and is a common source of errors. With core7 this kind of errors can be a) detected and b) fixed by using destroyLater().
Compile-time checks
There's a bunch of new compile time checks to validate that certain framework features are only used with correct classes. For example, Identifiers can only be used with classes that inherit from Identifiable and ObjectLists can only be used with classes that inherit from Listable.
Another check ensures that ObjectList-iterators use the same type like the begin() and end() iterators of the corresponding ObjectList.
Cleanup
Some files were moved, renamed or split into different classes.
There are several new headers that bundle certain framework features like e.g. ConsoleCommandIncludes.h, NetworkFunctionIncludes.h, or CommandLineIncludes.h.