Subsystems

From Arx Libertatis Wiki
Jump to navigation Jump to search

While thinking about refactoring the input I realized we had no global “subsystem” concept. It would really clean the code if we had a consistent way of initializing parts of the engine in a clean manner. For audio, we already have OpenAL and DirectSound, but their support is hardcoded in the code (see aalInit()).


List of Subsystems

  • Audio
  • Input
  • Graphics
  • Windowing
  • ...


Subsystems selection

cfg.ini ?

[subsystems_linux]
Audio = OpenALAudio
Input = SDLInput
Graphics = OpenGLGraphics
Windowing = X11Windowing

[subsystems_windows]
Audio = DSoundAudio
Input = DInput
Graphics = DX9Graphics
Windowing = Win32Windowing

Some thoughts: many unix(-like) systems (linux, BSD, mac) will have similar values for most backends, but maybe not all - maybe subsystem_unix who'se values can be overriden are useful?

Scripting the engine init

I’ve come across another engine that had its whole startup sequence scripted. I found it very efficient to work with since you could tweak the init at any time without recompiling anything. It was also possible to use different script for each platform used. The same result could be achieved with ini files or with command line switches, but I think this kind of script is more elegant and flexible.

In the case of arx, a script file could look like this (a dumb example, but you get the point): arx_startup.script

LoadSubsystem Audio OpenALAudio
LoadSubsystem Graphics OpenGL

# Hey I really don’t need that splash screen every time I want to debug
#ShowSplashScreen

# Same for the intro cinematic
#ShowIntroCinematic

# Show main menu!...
#ShowMainMenu

# I just want to load a level directly
LoadLevel 10

Commands can be registered easily in the code:

REGISTER_COMMAND(LoadSubsystem);
bool LoadSubsystem(const std::string& args)
{
    // Just do it
}


Ideas / TODOs:

- How would a portable implementation of something line REGISTER_COMMAND look like (also applies to DECLARE_CLASS in the next section)? Remember that C++ allows compilers to lazy initialize global variables: The variable doesn't need to be initialized until a function / method from the same translation unit is called. I wonder how different compilers behave here?

- Scripting the intro sequence is definitely useful, but what advantage does scripting the subsystem setup have over the ini-based approach? Some subsystems will probably need to be initialized in a specific oder and may depend on specific implementations for other subsystems (IE: SDL input will probably depend on SDL windowing, ...)

- If subsystems are configured in scripts, how would an in-game config GUI save selected subsystems without clobbering custom script modifications?

- If we are going to use scripts we should probably re-use the existing .asl script syntax and interpreter (which is due to be cleaned up for the case sensitive work)

Class Reflexion

Would be a nice addition to allow all this generic init to work. Something like:

std::string strSubsystem = get from cfg
Class* pSubsystemClass = Class::GetClassByName(strSubsystem);
Subsystem* pSubsystemInstance = Cast<Subsystem>(pSubsystemClass->AllocateNew());

All that is needed for this to work for any class is simple macros inserted in the class body / cpp

DECLARE_CLASS(clsName, clsParentName);
IMPLEMENT_CLASS(clsName);


Pushing it further

Having the option to store individual subsystems as .so/.dll and load them dynamically would be neat too!

All of this is really inspired by my engine / engines i’ve used in the past (UE3 for example) that worked like this... In my case I have a simple config file per platform listing all the subsystems that should be used. At one point I even had a dialog box that would pop up to allow the user to choose the subsystems the first time the engine was started :)