Intelligent DLL design in game programming

by Dimi 20. February 2008 23:10

So what is that title about? What is intelligent DLL design? When designing a game engine you have to take care of so many things. You have to think of inertial delay times, transmission delays, overheads, buffers and so much more....

Usually you design and develop your game engine, you test it and as soon as it's validated you compile it as a dll. This is in general a good idea because of so much advantages of a dll. Think of the faster cycle times, encapsulation, better design (we will change it to great design in this post) and so much more.

To use the game engine the game programmer would include the main header file of the game engine somewhere in his code and link to the static library when compiling the code. In Visual Basic you call this early binding. In C++ you call it binding on compile time.

I will show you here a more elegant and more efficient way to design a DLL. Instead of providing a "simple" source code for download, you can download the blueTec engine. This is a small 2d mini game engine which I've published on my project site G-Productions. This package contains some examples which will show you the implementation of this engine design. You can download the blueTec Engine directly at the end of this article. You need Dev-Cpp to compile this correctly. 

What is a game engine? In this article when we talk about a game engine we don't only mean the render stuff. A game engine does not only exist of those fun-making render calls and renderstates but also of those less-fun-making stuff like the sound and music api, like the script engine and the network module.... So if we talk about a game engine in this article we talk about all the stuff that makes the visual, the audio, the network game and much more. For simplicity we will use here pseudo code but you can download a complete working project with this article.

 

Imagine you have finished the development of your game engine and you want to provide it to a team of developers who will use it as base of game. Usually you would now create a DLL (dynamic link library) project, copy your header and source files into this and compile with a pre-processor directive like #define GAME_API __declspec(dllexport). What's wrong with this operation? Nothing! In fact this is ok for many projects but there are more intelligent ways.

Assuming your main game class of your game engine looks like this:

   1: class CEngine 
   2: {
   3: private:
   4:     // private stuff like the IDirect3DDevice9 or the OpenGL stuff go in here
   5: protected:
   6:     HRESULT    InitGraphics();
   7:     HRESULT    InitAudio();
   8:     HRESULT    InitInput();
   9:  
  10:     HRESULT    Destroy();
  11: public:
  12:     CEngine ( HINSTANCE );
  13:     ~CEngine ();
  14:  
  15:     HRESULT    InitEngine();
  16:     HRESULT    ShutdownEngine();
  17:  
  18:     HRESULT    MoveFrame( float );
  19:     HRESULT    BeginRender();
  20:     HRESULT    RenderFrame();
  21:     HRESULT    EndRender();
  22:  
  23:     HRESULT    PlaySound();
  24:     HRESULT    PlayMusic();
  25: };
Listing 1: the main engine class from the dll 

 

This class is the main exported class of your game engine and would usually look in line 1 like this:

class GAME_API CEngine

what tells the compiler that this class can be used from outside. You would now have to compile the dll (a static library is generated automatically which the developer team will use it to link to it) and provide the complete code/project to the developer team. Now imagine you would detect a bug or optimize some functions within your game engine or the developer team would detect a bug? You would have to fix the bug and the developer team would not be able to continue the work on that point where the bug occured. They would have to find a way around. Then you would have to send again the whole package and the team would have to replace the complete game engine again, all the header and source files, recompile with the new static library provided with the dll etc.

And here comes the cool thing. With our method these steps aren't necessary any more. So let's get to work guys.

1) Create a static library project

step1

First of all create a new static library project like shown in the figure on the right. This static library is the base of our DLL design.

In Dev-Cpp the static libraries have the *.a extension. In Visual Studio or other compilers the static libraries have a *.lib extension. This is nearly the same, except of one point. You will be able to use Visual Studio generated static libraries with Dev-Cpp (in fact the MinGW compiler will use them since Dev-Cpp is only the IDE) but only if they are not so called short-symbol static libs.

 

pic2

Now after creating the project add two new header files like in the picture on the left and one source file. We will now take a closer look what the files contain. Let's start with the header file gameEngineDevice.h

 

 

   1: class CGameEngineDevice
   2: {
   3: protected:
   4:     HINSTANCE   m_hDll;
   5:     int         m_iWidth;
   6:     int         m_iHeight;
   7:     int         m_iBpp;
   8:     bool        m_bWindowed;
   9:     HWND        m_hWnd;
  10:  
  11: public:
  12:     CGameEngineDevice(){};
  13:     virtual ~CGameEngineDevice() = 0;
  14:     virtual HRESULT         InitEngine() = 0;
  15:     virtual HRESULT         ShutdownEngine() = 0;
  16:  
  17:     virtual HRESULT         MoveFrame( float ) = 0;
  18:     virtual HRESULT         BeginRender() = 0;
  19:     virtual HRESULT         RenderFrame() = 0;
  20:     virtual HRESULT         EndRender() = 0;
  21:  
  22:     virtual HRESULT         PlaySound() = 0;
  23:     virtual HRESULT         PlayMusic() = 0;
  24: };
  25:  
  26: typedef class CGameEngineDevice *LPENGINEDEVICE;
  27:  
  28: extern "C"
  29: {
  30:     HRESULT CreateEngineDevice( HINSTANCE hDll, CGameEngineDevice **pInterface );
  31:     typedef HRESULT (*CREATEENGINEDEVICE)(HINSTANCE hDll, CGameEngineDevice **pInterface );
  32:  
  33:     HRESULT RelaseEngineDevice( CGameEngineDevice **pInterface );
  34:     typedef HRESULT (*RELEASEENGINEDEVICE)(CGameEngineDevice **pInterface );
  35: }

Listing 2: class CGameEngineDevice from the file gameEngineDevice.h

 

What did we do here? Compare the class CGameEngineDevice with the original game engine class CGameEngine. What do you see? Yes you are right. All the public functions in our class CGameEngine are defined virtual. Below in line 28 we see an implementation of helper functions which we will need later.

Let's go to the file gameEngine.h. Here is the content:

   1: #include "gameEngineDevice.h"
   2:  
   3: class CGameEngine
   4: {
   5: private:
   6:     CGameEngineDevice    *m_pDevice;
   7:     HINSTANCE            m_hInst;
   8:     HMODULE              m_hDll;
   9: public:
  10:     CGameEngine( HINSTANCE hInst );
  11:     ~CGameEngine( void );
  12:     
  13:     void                Clear();
  14:  
  15:     HRESULT             CreateDevice( void );
  16:     LPENGINEDEVICE      GetDevice( void ) { return m_pDevice; }
  17:     HINSTANCE           GetModule( void ) { return m_hDll; }
  18:     void                Release( void );
  19: };
  20:  
  21: typedef class CGameEngine *LPGAME;

Listing 3: class CGameEngine from the file gameEngine.h

 

and here is a snapshot only of the most important function of the file gameEngine.cpp

   1: HRESULT CGameEngine::CreateDevice()
   2: {
   3:     HRESULT hr = S_OK;
   4:     char buffer[300];
   5:  
   6:     m_hDll = LoadLibraryEx("gameEngine.dll", NULL, 0);
   7:     if( !m_hDll )
   8:         return E_FAIL;
   9:  
  10:     CREATEENGINEDEVICE _CreateEngineDevice = 0;
  11:  
  12:     _CreateEngineDevice = (CREATEENGINEDEVICE)GetProcAddress( m_hDll, "CreateEngineDevice");
  13:     hr = _CreateEngineDevice( m_hDll, &m_pDevice );
  14:  
  15:     if( FAILED( hr ))
  16:     {
  17:         m_pDevice = NULL;
  18:         return E_FAIL;
  19:     }
  20:     return S_OK;
  21: }

Listing 4: code snippet from the file gameEngine.cpp

 

Wow, pretty much isn't it? Ok in short, the CreateDevice function is the bread and the butter of this project. LoadLibraryEx in line 6 loads the dynamic link library gameEngine.dll which will contain the game engine. In line 10 you see the the use of _CreateEngineDevice which was defined in the gameEngineDevice.h. The call of GetProcAddress retrieves the address of the exported function CreateEngineDevice from the specified dynamic-link library gameEngine.dll which was loaded before with the call LoadLibraryEx.

The static library project is finished. Save and compile to generate the static library file.

2. The DLL project

Now back to our dll project. The only thing you have to do here is to extend the main engine class CEngine. Take a look at listing 1 and replace the first line from class class CEngine to class CEngine : public CGameEngineDevice and to include the header file gameEngineDevice.h from our previously created static library project.

What did we do? The class CEngine inherits public from CGameEngineDevice. What does this mean for us? This means that all the functions which are declared virtual in the class CGameEngineDevice have to be implemented in the class CEngine.

One last thing is also to do. Remember the line 28 in listing 2. The extern "C" declaration? Where are those functions? Those have to be integrated within the dll so here we go with the code:

   1: extern "C"
   2: {
   3: HRESULT DLLIMPORT CreateEngineDevice( HINSTANCE hDll, CGameEngineDevice** pDevice )
   4: {
   5:     if( !*pDevice )
   6:     {
   7:         *pDevice = new CGameEngineDevice( hDll );
   8:         return S_OK;
   9:     }
  10:     return E_FAIL;
  11: }
  12:  
  13: HRESULT DLLIMPORT ReleaseEngineDevice( CGameEngineDevice** pDevice )
  14: {
  15:     if( !*pDevice )
  16:         return E_FAIL;
  17:  
  18:     delete *pDevice;
  19:     *pDevice = NULL;
  20:     return S_OK;
  21: }
  22: }

Listing 5: implementation of the wrapper functions for allowing the dynamic binding of the dll.

3. Using the dll in a game

How would you use the game engine in your game project?

  1. include in your game project the file gameEngine.h of the static library project
  2. define two new variables using the objects in our static library project (CGameEngine and CGameEngineDevice)
  3. create an instance of CGameEngine and call the function CreateDevice() to create a valid game engine device
  4. get a valid pointer into our CGameEngineDevice object using the call CGameEngine->GetDevice()

 

Here is the sample code:

   1: int WINAPI WinMain (HINSTANCE hThisInstance,
   2:                     HINSTANCE hPrevInstance,
   3:                     LPSTR lpszArgument,
   4:                     int nFunsterStil)
   5:  
   6: {
   7:     HWND hwnd;
   8:     MSG messages;
   9:     WNDCLASSEX wincl;
  10:  
  11:     // our objects from the static library project
  12:     CGameEngine           *m_pEngine;
  13:     CGameEngineDevice     *m_pEngineDevice;
  14:  
  15:     // window dependent stuff goes in here
  16:  
  17:     if (!RegisterClassEx (&wincl))
  18:         return 0;
  19:  
  20:     hwnd = CreateWindowEx ( 0, szClassName, "Windows App", /* window init stuff here */ );
  21:  
  22:     ShowWindow (hwnd, nFunsterStil);
  23:  
  24:     /* we need to save the instance and the window handle because we need them for the engine */
  25:     g_hInst = hThisInstance;
  26:     g_hwnd = hwnd;
  27:  
  28:     m_pEngine = new CGameEngine( g_hInst );
  29:  
  30:     // some simple error handling
  31:     if( !m_pEngine )
  32:         // error handling
  33:  
  34:     // now with calling the CreateDevice function the dll symbol is dynamicaly
  35:     // loaded so it's a real dynamic binding
  36:     if (FAILED( m_pEngine->CreateDevice()))
  37:         // error handling
  38:  
  39:     // the created device is passed over to our engine pointer in order to work with
  40:     m_pEngineDevice = m_pEngine->GetDevice();
  41:  
  42:     if( m_pEngineDevice == NULL )
  43:         // error handling
  44:  
  45:     if( FAILED( m_pEngineDevice->InitEngine() ))
  46:         // error handling
  47:  
  48:     /* ... message que here ... */
  49:     return 0;
  50: }

Listing 6: How to use the game engine in a game project

 

Just for fun

pic3 Open the dll using the tool Dependency Walker which you can find here. This tool scans Windows modules like dll's, exe files and ocx files and builds a hierarchical tree of the dependency. The tool shows also the exportable functions and entry points of the dll.

If you open our blueTec.dll with this tool you will notice only two available/exportable functions: CreateEngineDevice and ReleaseEngineDevice (look at the picture on the right). These are the functions which we have implemented within our dll and declared in our static library. Nothing more nothing less and that's all we need.

 

pic4 Other dll's like shown on the second picture which are compiled with the usual dll design are just exporting the complete class and provide entry points to every single method and property. Do you see the difference?

 

 

Resume

Why did we do all that above and why did the author confuse us more than necessary. Where are the benefits and advantages and why should someone go the way above?

Quiet easy. Imagine the problems mentioned at start of this article with bugs and stuff like that.

  • This way is a real dynamic implementation. Looking at the code snippets we see that the real game engine class is referenced from the dll itself. The application just receives a pointer at runtime. You could develop and test your complete application without even owning the dll because it is referenced at runtime. Something which wouldn't be possible with the usual method.
  • The greatest advantage is that in case of a change, an optimization and/or a bug fix you would recompile the dll and send only the compiled dll to the developer team. The team would replace only one file instead of having to replace many single header and source files and recompile the current milestone with the new created static library which ships with the dll. You safe so much time.
  • Another great benefit is that you don't have to provide your very secret code because you only deliver the static library project and the compiled dll. With the traditional way you would have to provide the complete source of the dll project.
  • Another very important advantage is that you could develop a game engine using completely the DirectX API and name it gameEngineDX.dll and provide it to a game development team. While the development team is full in progress to create a game using your game engine you could develop a second game engine using the OpenGL API (gameEngineOGL.dll). This game engine should also inherit from the class CGameEngineDevice. Now it would be for the development team very easy to switch between the two render API's and the best is that the team could develop the complete game using only one dll without owning the second and won't have to rewrite any code when receiving the second dll, since the definition of the main game engine class is the same and ignores which API is used. The development team doesn't even need to have the DirectX-SDK installed since the complete game engine code is already compiled and delivered within the dll.

These points are the most important I think and so this article is at the end now. I hope I could provide a "different" look at game engine design or just call it dll programming. There are more ways to design a dll but you should always have in mind that the result should not only be a fast, scalable and efficient dll at runtime but also while development since the most time you and your team will sit in front of the code and if you are able to safe some time for the game developer and make his work more efficient you should spend the time for a good design of your game engine ;)

Downloads:

blueTec_sdk.rar (515,53 kb)

Tags: , , ,

game development

blueTec engine

by Dimi 24. December 2004 12:45

About

The blueTec Engine is a powerfull game engine providing the developer with an interface to create a drawing, sound and music device in order to create games. The engine is very powerfull and not limited through software so the developer has full control and freedom to design easily games without limitations
About
The blueTec Engine was developed in order to encourage people with less knowledge in graphic programming to create games. Therefore the blueTec Engine is held as easy and simple as possible so that any developer no matter what skills he has in programming can use this engine without having to take care of all that graphic and sound stuff. Since most developers don't take the time to go more into any of the graphic API to realize their ideas, this engine will hopefully be a good starting point and will soon find people to try.
Features
The blueTec Engine currently supports the following features:
- surface loading from 16 and 24 bit bmp files
- sound loading from uncompressed wave files (pcm)
- music loading from wave and midi files
Publish games
As an additional attempt to encourage young developers to develop games we provide the possibility to host their games on our website as long as you want us to do this. You developed a game using the blueTec Engine? You have no webspace to upload it? Drop us some lines in the forum on http://www.g-productions.net and we will provide your game for download.
FAQ
Still any questions? Check out the general FAQ or the developers FAQ below in order to get more information.
Download
Download the blueTec SDK here
Features to come
- surface loading from 8, 16, 24 and 32 bit compressed and uncompressed bmp files
- surface loading from 24, 32 bit tga files
- surface loading from pcx files
- sound loading from ogg files
- music loading from ogg files
- music loading from mp3 files (?)

Developers FAQ


I have the MinGW version x.y.z compiler. Can I use the sdk?
The SDK was developed using MinGW 3.2.2 but should also work with 3.1.x Just try and you will find out. If you encounter any problems try another version or drop us some lines.
I try to execute the samples but a message appears "Could not load game api"
In order to minimize the size of the file the dll was not copied into all the sample dirs. To try out the samples just copy the blueTec.dll from the dll folder of the sdk into the sample folders and everything should work fine
I've created a new project. After compiling a compile error tells me "blueTec.h: no such file or directory"
Go to your project options. Select the directories folder. Within the folder select Include Directories. Within this you should add the path to the Include directory of the SDK. After that everything should compile fine
I created a new project. After compiling a linker error tells me "cannot find -lblueTec"
Do the same as above for the library folder, but then point to the lib folder of the SDK
I took a look into the header and source files of the sdk. Where are the functions?
You won't find any. The blueTec Engine was designed so that you are able to perform a dynamic link to the engine api at runtime. This is also called late binding. Not function or class will really be build within your application since this would be static linking exporting the dll's symbols at compile time. To have dynamic binding we choose the other way like this is done in every modern game engine.

General FAQ


What is the blueTec engine?
The blueTec engine is a game engine supporting a draw device, a sound and a music manager. This engine provides people with less knowledge on graphic and sound programming with some important and usefull tools to create their own games. Now everyone is able to create small little games withouth haveing to take care of all the stuff around a game. You can fully concentrate on your content.
What do I need to use the blueTec engine?
You need to have Dev-Cpp installed or at least the MinGW compiler tools in order to use the blueTec engine. Since it is not necessary to have an IDE installed we recommend you to use Dev-Cpp or MinGW Studio or your favourite IDE. You also need to have a basic knowledge on C++ or at least some general programming skills.
How do I use the blueTec engine?
In order to use the blueTec engine you need to download the blueTec SDK which is available for download on this site. Just download the package and unzip it to your favorite folder. To create a blueTec application (most probably a game) you might want to take a look into the documentation or at least study the example applications which give you a great entry point. We recommend both but beginners should read the documentation in order to understand what they are doing.
I want to create a big, big game, is this possible with the blueTec engine?
The blueTec engine is not limited. You can create any game you want no matter how small or big it will be at least. To make it more clear, the only thing limiting the blueTec engine is the memory available on your machine.
What are surfaces you talk about?
The blueTec engine supports only a 2d device. That means that you will only be able to create 2d games if you have the time and the skills maybe 2.5d is also possible. When talking about surfaces we mean an object which is created from a file or text to represent a drawn area on the screen.
I have a specific questions about develing with blueTec or general questions which is not covered by the FAQ
No problem. Head over to the forums and post it. As soon as possible it will be answered. Or contact us directly via the board or email address.
I just created a game using the blueTec engine. Would you host it?
Sure, contact us. We will upload it and host it for you. Optional you have the possibility to create some kind of story site or just give us a short description to post with the download.
Why are you guys providing a 2d engine and not a 3d engine?
This engine is for all who want to make small games. Not everyone has the time and the skills to create 3d games. Providing this little engine helps people to concentrate on the real work of a game and not on the, often very time consuming, framework to create a game. 2d games can also be very cool. If you have a cool idea you have now the tools to realize it.
Will there be any future updates?
Yes of course. Maybe this is only a small project compared to the blueEngine ( read more ) but that doesn't mean that we won't improve and enhance this project. You are greatly welcome to give your opinion, tips and feature requests.
The blueTec engine is cool but I need the feature xy. Would you implement it?
Most probably yes. The blueTec engine is from developers for developers. So if you use it and miss a specific feature or function head over to the forums and post it. As soon as possible an update will be made.

 

Updates

.plan update 10.03.2005
Who said we are dead? A lot of resources were put into the blueEngine so the blueTec SDK had to wait the last months for its update. But since the blueEngine is finished now I can concentrate on the SDK again in order to fulfill the needs.
The ogg support is implemented and playing avi files on surfaces also is close to finish, an update will follow soon.
Loading different image formats may be also included but is not my primary goal at current since you have with dds and bmp two powerful file formats you can play with. Future? Of course the features mentioned below will be included but also some other goodies I have in mind so stay tuned.
.plan update 24.12.2004
Introducing the new file formats is the biggest change for the new version. About mp3 file format there has to be decided since mp3 is evil and not our first choice to implement. Therefore the decision to have ogg files supported is great and can be taken from blueEngine (why to use an evil format when you can have a powerful equal format which is free?)
- loading tga of any format file and memory
- loading pcx of any format file and memory
- loading bmp of any format file and memory
- loading ogg file and memory
- loading mp3 file
- playing avi files on surfaces
- providing helper functions for conversion between color values etc
- enable/disable log, html log select since not everyone wants to use log file output. Additionally providing the interface to use the log file functionality for the complete application (not only the engine)

blueTec_sdk.rar (515,53 kb)

Tags: , ,

game development

Powered by BlogEngine.NET 1.6.1.0
Theme adopted by Dimi with portions of Mads Kristensen and portions of Rtur.net

RecentPosts