Lesson 1: Getting started with the plugin SDK

The Plugin SDK can be found in the sdk_plugin folder of your Gamestudio installation. The folder contains everything you need. It comes with a sample source file, sampledll.cpp, that contains some typical examples for DLL functions. Examine the examples carefully - it's the best way to learn how to program DLL functions and access engine parameters. You can use them as a 'template' for your own DLL.

You'll also find a Borland .a library in the folder - read the readme.txt for more information about how to create Borland projects. We are here using VC++ .Net for our following examples. We are also assuming that you have some basic knowledge of C and lite-C. If not, read a C/C++ book before continuing here.

Setting up VC++

When you create a new project (File->New Project), VC++ offers you a choice of project templates. Don't select "MFC DLL" here - MFC is not your friend. With VC++ .Net it's the plain Win32 Project, with VC++ 6.0 the Win32 Dynamic-Link Library that you'll want to create. When the Win32 Application Wizard pops up, select DLL (.NET) or Simple DLL (6.0) in the application settings and you're done. VC++ now creates a new DLL project for you. The main cpp file will look like this:

// plug-in.cpp : Defines the entry point for the DLL application.

#include "stdafx.h"

BOOL APIENTRY DllMain( 
  HANDLE hModule,
  DWORD ul_reason_for_call,
  LPVOID lpReserved)
{
  return TRUE;
}

That's the main entry point of your DLL and you can leave that function unchanged. Copy all of the SDK files (except the Borland/Delphi subfolders of course) into the folder that is created by VC++. For compiling an engine plug-in DLL, you have to link the adll.lib (one of the files of the SDK) to the project in Project Properties -> Linker Input -> Additional Dependencies.

If you want to distribute your DLL to other people, it might be a good idea to open Properties / C/C++ / Code Generation, and change the Runtime Library setting from Multi-threaded DLL (/MD) to Multi-threaded (/MT). Otherwise your users won't be able to use the DLL without installing the VC++ Runtime Redistributable before - that's one of the funny ideas by Microsoft to make life more interesting.

When using VC++ 2005, you also need to tell the linker not to use the libc.lib that was abandoned by Microsoft. Thus, ignore the specific library libc.lib in the project properties for Debug and Release, and add libcmt.lib to the Linkers Additional Dependencies.

Writing a basic DLL

Now edit your source file, define DLL_USE and include the adll.h header (see below). DLL_USE switches the adll.h header to DLL mode. You can see in the sampledll.cpp example how it should look like.

The adll.lib contains some useful functions, like engine_getobj and engine_getscript. We'll learn about those functions later. However, the DLL has to call at least one of the library functions to make sure that the library is linked to it, and not optimized away. To make this sure, add the following call to DllMain:

#include "stdafx.h"
#define DLL_USE
//#include "var.h" (only when var class is used)
#include "adll.h"

BOOL APIENTRY DllMain( 
  HANDLE hModule,
  DWORD ul_reason_for_call,
  LPVOID lpReserved)
{
  engine_bind();
  return TRUE;
}
Now you can begin to add your own functions to the DLL that can then later be called by a script, or by the renderer. To be exported to the engine, use the DLLFUNC macro to declare a function, like this:
// returns the value of x * 2n
DLLFUNC var ldexpc(var x,var n)
{
  return (_VAR(_FLOAT(x)*pow(2.0,_FLOAT(n))));
}
This example function just returns an arithmetic expression of its arguments. DLLFUNC is just a convenience shortcut for declaring DLL export functions. var is the all-purpose numeric variable type of C-Script - a long integer that can be used either as 22.10 fixed point value, or as a pointer. Both are declared in the header file adll.h together with some conversion functions. It might be useful to look at them:
#define DLLFUNC extern "C" __declspec(dllexport)

typedef long var; // fixed point 22.10 number format used by C-Script
inline var _VAR(int i) { return i<<10; }					// int -> var
inline var _VAR(double f) { return (var)(f*(1<<10)); }		// double -> var, overloaded
inline int _INT(var x) { return x>>10; }					// var -> int
inline float _FLOAT(var x) { return ((float)x)/(1<<10); }	// var -> float
inline char* _CHR(STRING* s) { return s->chars; }			// STRING* -> char*

The engine will expect all numbers - coordinates, variables, no matter what - in var type. So use the _VAR macro to convert any number to var before you return it to the engine, like in the example above. Alternatively, you can include the var C++ class (var.h) before adll.h - then the conversion is handled automatically and you must not care about the conversion functions.

Using the DLL in C-Script and lite-C

Ready? Now compile your DLL - let's assume that you named it plugin.dll - and copy it either into your acknex_plugins folder if you want to use it in many projects, or into the work folder if you only want to use it in a certain project . How can we now call our ldexpc function by a script? We have to declare the function prototype in the script, or in a script header file. In lite-C it's just a normal prototype (and you can use other variable types than var), in C-Script we need a special dllfunction declaration:

dllfunction ldexpc(x,n); // declaration of a DLL function in C-Script
function ldexpc(x,n);    // declaration of a DLL function in lite-C
This makes our ldexpc function known to the script. You can now enjoy that the script language has got one extra instruction!
...
x = ldexpc(y,n); // calculates x = y * 2n
...

For debugging your DLL in VC++, set the command in Project Properties -> Debug to the engine EXE path (like "C:\program files\gstudio\bin\acknex.exe"), the command arguments to your script and command line parameters (like "mygame.wdl -wnd") and the working directory to your game directory (like "C:\program files\gstudio\mygame"). In Project Properties -> General, set the output directory and the intermediate directory to . (a period, meaning the working directory) for the engine to find the intermediate files. Then you can compile and debug your DLL by setting breakpoints as usual.

Ok, this was the basics of writing plugin DLLs. Of course, there's a lot more to learn. The methods for exchanging data with the engine are described in the following. All DLL functions can be declared and called in scripts just like any other C-Script function..

► latest version online