update : Pierre has fixed bugs in HPB bot template 4, and is aware of bugs in RACC template 2
version 1.0, 31.08.2004
Newest version of this document can be found on http://neuron.tuke.sk/~wagner or http://kxbot.sourceforge.net
This is part of Guide : Exporting functions in Half-life mods
Copyright (C) 2004 Jozef Wagner, http://neuron.tuke.sk/~wagner
Permission is granted to copy and distribute this document unmodified. All modifications are prohibited without written permission from Jozef Wagner
All copyrighted material is used here as fair use of the material under United States copyright law.
THIS DOCUMENT IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS DOCUMENT, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This document is for experienced Half-life bot/mod coders.
Seems like bot coders have problem determining how to export various functions in their bot. This document should help them understand how things should be.
1. How GiveFnptrsToDll() is declared ?
2. How is EXPORT, DLLEXPORT and WINAPI defined ?
Most of the bots/mods can be downloaded from www.bots-united.com
SDK - Half-Life 1 SDK, version 2.3. Alfred Reinolds patch applied
HPB4 - Botmans bot, version 4
HPBT3 - Botmans bot template, version 3
HPBT4 - Botmans bot template, version 4
JOE - Joebot, version 1.6.5.3
META - Metamod, version 1.17.2
POD26 - Podbot, version 2.6
RACCP - RACC Preview
RACC1 - RACC Template, version 1
RACC2 - RACC Template, version 2
REAL - RealBot, CVS snapshot, August 2004
WHIST - Whistlers bot framework, first version
VC6 - Visual C++ 6.0 headers
VC7 - Visual C++ 7.0 headers (Visual Studio .NET 2003)
HPBF3 - Botmans forum archive, 2003
HPBF4 - Botmans forum archive, 2004
BUF4 - Bots united forum, August 2004
defined in dlls\h_export.cpp :
#ifdef _WIN32 void DLLEXPORT GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine,globalvars_t *pGlobals) #else extern "C" void GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine,globalvars_t *pGlobals) #endif
defined in h_export.cpp :
void WINAPI GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine,globalvars_t *pGlobals)
defined in dlls\h_export.cpp :
#ifndef __linux__ #ifdef __BORLANDC__ extern "C" DLLEXPORT void EXPORT GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine,globalvars_t *pGlobals) #else void DLLEXPORT GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine,globalvars_t *pGlobals) #endif #else extern "C" DLLEXPORT GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine,globalvars_t *pGlobals) #endif
ERROR : Under linux, GiveFnptrsToDll will not be returning void (?will return int?). This differs from the SDKs definition. Also definition under Borland looks very strange.
defined in dlls\h_export.cpp : (same as HPBT3)
#ifndef __linux__ #ifdef __BORLANDC__ extern "C" DLLEXPORT void EXPORT GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine, globalvars_t *pGlobals) #else void DLLEXPORT GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine, globalvars_t *pGlobals ) #endif #else extern "C" DLLEXPORT GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine, globalvars_t *pGlobals ) #endif
ERROR : Under linux, GiveFnptrsToDll will not be returning void (?will return int?). This differs from the SDKs definition. Also definition under Borland looks very strange.
defined in dlls\h_export.cpp :
#ifdef _WIN32 #if defined(__BORLANDC__) || defined(__MINGW32__) extern "C" DLLEXPORT void EXPORT GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine,globalvars_t *pGlobals) #else void DLLEXPORT GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine,globalvars_t *pGlobals) #endif #else extern "C" DLLEXPORT void GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine,globalvars_t *pGlobals) #endif
ERROR : DLLEXPORT must be after the the void. However it does not matter, because the DLLEXPORT is empty definition under the linux.
C_DLLEXPORT declared in metamod\osdep.h :
#define C_DLLEXPORT extern "C" DLLEXPORT
declared in metamod\h_export.h :
C_DLLEXPORT void WINAPI GiveFnptrsToDll(enginefuncs_t *pengfuncsFromEngine,globalvars_t *pGlobals);
defined in metamod\h_export.cpp :
void WINAPI GiveFnptrsToDll(enginefuncs_t *pengfuncsFromEngine,globalvars_t *pGlobals)
WARNING : DLLEXPORT *IS NOT* the same as in the SDKs dlls\cbase.h definition. See below for more information
WARNING : The definition is different than the declaration. However, there is no need to specify an extern "C" __declspec(dllexport) in both definition and declaration
defined in dlls\h_export.cpp :
#ifndef __linux__ void DLLEXPORT GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine,globalvars_t *pGlobals) #else extern "C" DLLEXPORT GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine,globalvars_t *pGlobals) #endif
ERROR : Under linux, GiveFnptrsToDll will not be returning void (?will return int?). This differs from the SDKs definition
declared in common\bot_common.h :
void DLLEXPORT GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine,globalvars_t *pGlobals);
defined in *\dll.cpp :
void DLLEXPORT GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine,globalvars_t *pGlobals)
declared in common\engine_specific.h :
#ifdef _WIN32 #define DLL_GIVEFNPTRSTODLL void #else #define DLL_GIVEFNPTRSTODLL extern "C" #endif // _WIN32
declared in common\racc.h :
DLL_GIVEFNPTRSTODLL DLLEXPORT GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine,globalvars_t *pGlobals);
defined in cstrike\dll.cpp :
DLL_GIVEFNPTRSTODLL DLLEXPORT GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine,globalvars_t *pGlobals)
ERROR : Under linux, GiveFnptrsToDll will not be returning void (?will return int?). This differs from the SDKs definition
declared in common\racc.h :
void DLLEXPORT GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine,globalvars_t *pGlobals);
defined in *\dll.cpp :
void DLLEXPORT GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine,globalvars_t *pGlobals)
defined in dll.cpp :
C_DLLEXPORT void WINAPI GiveFnptrsToDll(enginefuncs_t * pengfuncsFromEngine,globalvars_t * pGlobals)
NOTE : C_DLLEXPORT definition is taken from META
declared in main.h :
#define C_DLLEXPORT extern "C" DLLEXPORT C_DLLEXPORT void WINAPI GiveFnptrsToDll(enginefuncs_t *pengfuncsFromEngine,globalvars_t *pGlobals);
defined in h_export.cpp :
void WINAPI GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine,globalvars_t *pGlobals )
This bot copied declaration from META
WARNING : DLLEXPORT *IS NOT* the same as in SDKs dlls\cbase.h definition. See below for more information
WARNING : The definition is different than the declaration. However, there is no need to specify an extern "C" __declspec(dllexport) in both definition and declaration
DLLEXPORT is defined differently in various bots
GiveFnptrsToDll is declared/definied differently in various bots. Only difference is usage of extern "C" and void as a return type
I've read many posts in HPBF4 regarding missing void type ("h_export.cpp: ISO C++ forbids declaration of `GiveFnptrsToDll' with no type"). This can be simply fixed by adding void return type to declaration of GiveFnptrsToDll() where it is missing.
Of course, GiveFnptrsToDll should NOT be exported with LINK_ENTITY_TO_FUNC() macro. This applies also for GetNewDLLFunctions, GetEntityAPI, GetEntityAPI2 and Server_GetBlendingInterface
Correct and simplest definition for MSVC and linux (not for Borland and MingW32) is
#ifdef _WIN32 extern "C" __declspec(dllexport) void __stdcall GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine,globalvars_t *pGlobals) #elif defined __linux__ extern "C" void GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine,globalvars_t *pGlobals) #endif
Note that this definition works both with/without the .def file (However to make it work without .def file, small piece of code must be attached. see below in the .def section)
Note : extern "C" is important in MSVC if you want to export the GiveFnptrsToDll without .def. Read more about it in my solution to the 3rd problem
DLLEXPORT is defined in engine\eiface.h :
#ifdef _WIN32 #define DLLEXPORT __stdcall #else #define DLLEXPORT /* */ #endif
NOTE : DLLEXPORT is defined differently in the other SDK files, but dlls\h_export.cpp is using definition mentioned above
EXPORT is defined in dlls\cbase.h :
#ifdef _WIN32 #define EXPORT _declspec( dllexport ) #else #define EXPORT /* */ #endif
WINAPI is defined in VC6 and VC7, see below
Using DLLEXPORT definition from SDK, file engine\eiface.h
EXPORT is defined in dlls\cbase.h :
#ifndef __linux__ #if defined(_MSC_VER) || defined(__BORLANDC__) #define EXPORT _declspec( dllexport ) #else #define EXPORT __declspec( dllexport ) #endif #else #define EXPORT #endif
Note that this EXPORT slightly differs from the SDKs dlls\cbase.h definition
Using DLLEXPORT definition from SDK, file engine\eiface.h
Using EXPORT definition from SDK, file dlls\cbase.h
Using the DLLEXPORT definition from the SDK, file engine\eiface.h
EXPORT is defined in dlls\cbase.h : (same as in HPBT3)
#ifndef __linux__ #if defined(_MSC_VER) || defined(__BORLANDC__) #define EXPORT _declspec( dllexport ) #else #define EXPORT __declspec( dllexport ) #endif #else #define EXPORT #endif
Note that this EXPORT slightly differs from the SDKs dlls\cbase.h definition
WINAPI is defined in VC6 and VC7, see below
DLLEXPORT is defined in metamod\osdep.h :
#ifdef _WIN32 #define DLLEXPORT __declspec(dllexport) #elif defined(linux) #define DLLEXPORT /* */ #endif /* linux */
Note that this DLLEXPORT *IS NOT* the same as in SDKs dlls\cbase.h definition
Using DLLEXPORT definition from the SDK, file engine\eiface.h
Using DLLEXPORT definition from the SDK, file engine\eiface.h. File is moved to directory \common
Using DLLEXPORT definition from the SDK, file engine\eiface.h. File is moved to directory \common
DLLEXPORT definition is not present. Assuming that it is using the declaration from SDK, but care should be taken not to include DLLEXPORT from META
WINAPI is defined in the VC6 and VC7, see below
WINAPI is defined in the VC6 and VC7, see below
This bot copied definition of the C_DLLEXPORT and DLLEXPORT from META
WINAPI is defined in include\windef.h :
#ifdef _MAC #define WINAPI CDECL #elif (_MSC_VER >= 800) || defined(_STDCALL_SUPPORTED) #define WINAPI __stdcall #else #define WINAPI #endif
NOTE : CDECL is defined as __cdecl
NOTE : WINAPI is defined as FAR PASCAL in some other files. For WIN16, FAR and PASCAL is defined as __far and __pascal, respectively. But in WIN32, FAR is defined as empty and PASCAL is defined as __stdcall, so everything is OK
WINAPI is defined in include\windef.h :
#ifdef _MAC #define WINAPI CDECL #elif (_MSC_VER >= 800) || defined(_STDCALL_SUPPORTED) #define WINAPI __stdcall #else #define WINAPI #endif
WINAPI is defined in platformsdk\include\MAPINls.h :
#if !defined(WINAPI) #if defined(_WIN32) && (_MSC_VER >= 800) #define WINAPI __stdcall #elif defined(WIN16) #define WINAPI _far _pascal #else #define WINAPI _far _pascal #endif #endif
NOTE : CDECL is defined as __cdecl
NOTE : WINAPI is defined as FAR PASCAL in some other files. For WIN16, FAR and PASCAL is defined as __far and __pascal, respectively. But in WIN32, FAR is defined as empty and PASCAL is defined as __stdcall, so everything is OK
Do not use DLLEXPORT, EXPORT and WINAPI. Many programs is defining it differently and it may cause trouble. Use construction like I mentioned above for GiveFnptrsToDll(), and use this construction for other functions :
#ifdef _WIN32 #define MYEXPORT extern "C" __declspec(dllexport) #elif defined __linux__ #define MYEXPORT extern "C" #endif ... MYEXPORT int MyFunction(enginefuncs_t* pengfuncsFromEngine,globalvars_t *pGlobals)
NOTE : It is something like the META used, but it does not collide with "oh so useful" DLLEXPORT
dlls\mp.def :
LIBRARY mp EXPORTS GiveFnptrsToDll @1 SECTIONS .data READ WRITE
In dlls\mp.dsp, def is included to project : /def:".\mp.def"
.def file is present and same as in SDK
.def file is present and same as in SDK
NOTE : This bot has different .def file for the borland compiler, but this is beyond the scope of this document
.def file is present and same as in SDK
NOTE : This bot has different .def file for the borland compiler, but this is beyond the scope of this document
.def file is present and same as in SDK
NOTE : This bot has different .def file for the borland compiler, but this is beyond the scope of this document
.def file is present and same as in SDK
.def file is present and same as in SDK
.def file is present and same as in SDK
.def file is present and same as in SDK
.def file is present and same as in SDK
.def file is not present, but this CVS snapshot I have is not suited for the MSVC, so everything is OK. I assume that in the "MSVC version", .def file is present
.def file is present and same as in SDK
Do not use .def files
Reasons : Because of 1 function, you have to have your code separated into 3 files :
Also there are many posts in HPBF4 and BUF4 from people who cannot sucessfully run their mod, and only problem is correct including of .def file.
Solution : use this code in the file where GiveFnptrsToDll() is defined (h_export.dll, best location is right before GiveFnptrsToDll()) :
#if defined _WIN32 #pragma comment(linker, "/EXPORT:GiveFnptrsToDll=_GiveFnptrsToDll@8,@1") #pragma comment(linker, "/SECTION:.data,RW") #endif // _WIN32
NOTE : You must also use extern "C" in definition of GiveFnptrsToDll() for above code to work, just like I stated in my solution to the first problem
Server_GetBlendingInterface is NOT defined in SDK
Not defined
Not defined
Defined in dlls\h_export.cpp :
#ifdef __BORLANDC__
extern "C" DLLEXPORT int EXPORT Server_GetBlendingInterface( int version, struct sv_blending_interface_s **ppinterface, struct engine_studio_api_s *pstudio, float (*rotationmatrix)[3][4], float (*bonetransform)[MAXSTUDIOBONES][3][4] )
#else
int DLLEXPORT Server_GetBlendingInterface( int version, struct sv_blending_interface_s **ppinterface, struct engine_studio_api_s *pstudio, float (*rotationmatrix)[3][4], float (*bonetransform)[MAXSTUDIOBONES][3][4] )
#endif
{
static SERVER_GETBLENDINGINTERFACE other_Server_GetBlendingInterface = NULL;
static bool missing = FALSE;
// if the blending interface has been formerly reported as missing, give up
if (missing)
return (FALSE);
// do we NOT know if the blending interface is provided ? if so, look for its address
if (other_Server_GetBlendingInterface == NULL)
other_Server_GetBlendingInterface = (SERVER_GETBLENDINGINTERFACE) GetProcAddress (h_Library, "Server_GetBlendingInterface");
// have we NOT found it ?
if (!other_Server_GetBlendingInterface) {
missing = TRUE; // then mark it as missing, no use to look for it again in the future
return (FALSE); // and give up
}
// else call the function that provides the blending interface on request
return ((other_Server_GetBlendingInterface) (version, ppinterface, pstudio, rotationmatrix, bonetransform));
}
***ERROR*** : This function is not exported under MSVC. DLLEXPORT macro is wrong and there is no declarator telling that this function should be exported. If this function will be exported by some miracle or .def file, it will crash whole Half-life for sure, because of wrong __stdcall calling convention. Calling convention for this function is __cdecl, which is default
Defined in dlls\dll.cpp :
#ifdef __BORLANDC__
//extern "C" DLLEXPORT int EXPORT Server_GetBlendingInterface(int version, struct sv_blending_interface_s **ppinterface,struct engine_studio_api_s *pstudio,float (*rotationmatrix)[3][4], float (*bonetransform)[MAXSTUDIOBONES][3][4])
int EXPORT Server_GetBlendingInterface(int version, struct sv_blending_interface_s **ppinterface,struct engine_studio_api_s *pstudio,float (*rotationmatrix)[3][4], float (*bonetransform)[MAXSTUDIOBONES][3][4])
#else
//int DLLEXPORT Server_GetBlendingInterface(int version, struct sv_blending_interface_s **ppinterface,struct engine_studio_api_s *pstudio,float (*rotationmatrix)[3][4], float (*bonetransform)[MAXSTUDIOBONES][3][4])
extern "C" EXPORT int Server_GetBlendingInterface(int version, struct sv_blending_interface_s **ppinterface,struct engine_studio_api_s *pstudio,float (*rotationmatrix)[3][4], float (*bonetransform)[MAXSTUDIOBONES][3][4])
#endif
{
if (other_Server_GetBlendingInterface == NULL)
return FALSE;
// call blending interface function
return ((*other_Server_GetBlendingInterface)(version, ppinterface, pstudio, rotationmatrix, bonetransform));
}
#endif /* not USE_METAMOD */
NOTE that in this case, bot author corrected error mentioned above
Defined in metamod\studioapi.cpp :
C_DLLEXPORT int Server_GetBlendingInterface(int version,struct sv_blending_interface_s **ppinterface,struct engine_studio_api_s *pstudio,float (*rotationmatrix)[3][4],float (*bonetransform)[MAXSTUDIOBONES][3][4])
{
static GETBLENDAPI_FN getblend=NULL;
static int missing=0;
// Note that we're not checking if
// (version==SV_BLENDING_INTERFACE_VERSION) because at this point, we
// don't really care, as we're not looking at or using the contents of
// the function tables; we're only passing them through to the gamedll,
// which presumably will check for version match, since it's the one
// that cares and actually uses the function tables.
// Return(0) if the gamedll does not provide this routine, and the
// Engine will use its own builtin blending. The Engine will report
// "Couldn't get server .dll studio model blending interface. Version
// mismatch?", but this will only show in "developer" (-dev) mode.
META_DEBUG(6, ("called: Server_GetBlendingInterface; version=%d", version));
if(missing) {
META_DEBUG(6, ("Skipping Server_GetBlendingInterface; was previously found missing"));
return(0);
}
if(!getblend) {
META_DEBUG(6, ("Looking up Server_GetBlendingInterface"));
getblend = (GETBLENDAPI_FN) DLSYM(GameDLL.handle,
"Server_GetBlendingInterface");
}
if(!getblend) {
META_DEBUG(6, ("Couldn't find Server_GetBlendingInterface in game DLL '%s': %s", GameDLL.name, DLERROR()));
missing=1;
return(0);
}
META_DEBUG(6, ("Calling Server_GetBlendingInterface"));
return((getblend)(version, ppinterface, pstudio, rotationmatrix,
bonetransform));
}
Not defined
Not defined
Not defined
Defined in *\dll.cpp :
int DLLEXPORT Server_GetBlendingInterface (int version, struct sv_blending_interface_s **ppinterface, struct engine_studio_api_s *pstudio, float (*rotationmatrix)[3][4], float (*bonetransform)[MAXSTUDIOBONES][3][4])
{
// this function synchronizes the studio model animation blending interface (i.e, what parts
// of the body move, which bones, which hitboxes and how) between the server and the game DLL.
// some MODs can be using a different hitbox scheme than the standard one.
static SERVER_GETBLENDINGINTERFACE other_Server_GetBlendingInterface = NULL;
static bool missing = FALSE;
// if the blending interface has been formerly reported as missing, give up
if (missing)
return (FALSE);
// do we NOT know if the blending interface is provided ? if so, look for its address
if (other_Server_GetBlendingInterface == NULL)
other_Server_GetBlendingInterface = (SERVER_GETBLENDINGINTERFACE) GetProcAddress (h_Library, "Server_GetBlendingInterface");
// have we NOT found it ?
if (!other_Server_GetBlendingInterface) {
missing = TRUE; // then mark it as missing, no use to look for it again in the future
return (FALSE); // and give up
}
// else call the function that provides the blending interface on request
return ((other_Server_GetBlendingInterface) (version, ppinterface, pstudio, rotationmatrix, bonetransform));
}
***ERROR*** : This function is not exported under MSVC. DLLEXPORT macro is wrong and there is no declarator telling that this function should be exported. If this function will be exported by some miracle or .def file, it will crash whole Half-life for sure, because of wrong __stdcall calling convention. Calling convention for this function is __cdecl, which is default
Not defined
Defined in h_export.cpp :
C_DLLEXPORT int Server_GetBlendingInterface(int version,struct sv_blending_interface_s **ppinterface,struct engine_studio_api_s *pstudio,float (*rotationmatrix)[3][4],float (*bonetransform)[MAXSTUDIOBONES][3][4])
{
// do we NOT know if the blending interface is provided?
if (!other_Server_GetBlendingInterface)
return FALSE; // give up
// else call the function that provides the blending interface on request
if (!((*other_Server_GetBlendingInterface)(version, ppinterface, pstudio, rotationmatrix, bonetransform)))
return FALSE;
return TRUE;
}
It is also declared in main.h with same function prototype as in definition
http://www.mail-archive.com/hlcoders@list.valvesoftware.com/msg01224.html
http://www.mail-archive.com/hlcoders@list.valvesoftware.com/msg02724.html
HPBT4 and RACC2 should be fixed !
Of course, Server_GetBlendingInterface should NOT be exported with LINK_ENTITY_TO_FUNC() macro. This applies also for GiveFnptrsToDll, GetNewDLLFunctions, GetEntityAPI and GetEntityAPI2
Function is exported normally with __cdecl (default) calling convention, without name-decoration
You don't have to specify __cdecl, because it is default calling convention in MSVC
Correct declaration (for MSVC and linux) is :
#ifdef _WIN32 extern "C" __declspec(dllexport) int Server_GetBlendingInterface( int version, struct sv_blending_interface_s **ppinterface, struct engine_studio_api_s *pstudio, float (*rotationmatrix)[3][4], float (*bonetransform)[MAXSTUDIOBONES][3][4] ) #else extern "C" int Server_GetBlendingInterface( int version, struct sv_blending_interface_s **ppinterface, struct engine_studio_api_s *pstudio, float (*rotationmatrix)[3][4], float (*bonetransform)[MAXSTUDIOBONES][3][4] ) #endif
End of document