A very high percentage of the internet Half-Life servers are run using the Linux operating system and the Linux version of the Half-Life server. Surprisingly, aside from my own Phineas Bot, there have been few Linux versions of other mods released. Hopefully, this article will change that. I will provide a step-by-step walkthrough, explaining exactly what must be edited in the Standard SDK in order to successfully compile it under Linux. Editing the Half-Life Standard SDK source code and compiling it for Linux is so easy that every mod-maker who is familiar with compiling programs under Linux, or knows someone who is, should consider it.
This article will not teach you the basics. It is written for people who know what their compilers are, and how to use them. If you don't know what a makefile is and how to use one, I would recommend that you find someone who does know and is willing to help you out with your mod.
This article does not address the Professional SDK, only the Standard SDK. I do not have the Pro SDK, nor am I interested in it. You may or may not be able to use this text as a guideline in porting the Pro SDK, but I am not supporting such use of this article.
Before I begin the walkthrough, I would like to address one of the problems that people often run into when using Windows text files in Linux. Windows text files have a ^M (carriage return) character at the end of every line that is very unfriendly to many Linux preprocessors and compilers. You will get otherwise unexplainable errors when you try to compile files with these characters in them. When you transfer source files over to a Linux OS, you must find some way of stripping those characters out of the files. There are programs that do this (many text editors will), but one good way is to use FTP. Chances are, you will already be using FTP to transfer the files to a Linux computer. The trick is to transfer .h and .cpp files in ASCII mode, rather than BINARY mode. This will strip out the ^M characters with no extra effort.
Now, on to the porting. If you are using a browser capable of color display, you will notice that red text is used to indicate something that needs to be changed, and green text is used to indicate what the change should be:
(STEP #1)
In the "engine/" directory, rename the file "PROGS.H" to "progs.h"
The only change is to make all of the letters in the filename lowercase.
(STEP #2)
In the "engine/" directory, edit the file "eiface.h":
Near line 37, change this code block:
#define DLLEXPORT __stdcall
To this code block:
/* LINUX COMPILE */ #ifdef _WIN32 #define DLLEXPORT __stdcall #else #define DLLEXPORT __attribute__((stdcall)) #endif /* END LINUX COMPILE */
(STEP #3)
In the "dlls/" directory, edit the file "cbase.h":
Near line 54, change this code block:
#define EXPORT _declspec( dllexport )
To this code block:
/* LINUX COMPILE */ #ifdef _WIN32 #define EXPORT _declspec( dllexport ) #else #define EXPORT #endif /* END LINUX COMPILE */
(STEP #4)
In the "dlls/" directory, edit the file "extdll.h":
Near line 35, change this code block:
// Prevent tons of unused windows definitions #define WIN32_LEAN_AND_MEAN #define NOWINRES #define NOSERVICE #define NOMCX #define NOIME #include "WINDOWS.H" // Misc C-runtime library headers #include "STDIO.H" #include "STDLIB.H" #include "MATH.H"
To this code block:
/* LINUX COMPILE */ #ifdef _WIN32 // Prevent tons of unused windows definitions #define WIN32_LEAN_AND_MEAN #define NOWINRES #define NOSERVICE #define NOMCX #define NOIME #include "WINDOWS.H" // Misc C-runtime library headers #include "STDIO.H" #include "STDLIB.H" #include "MATH.H" #else #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <math.h> #include <string.h> #include <ctype.h> #define ULONG ulong #define FALSE 0 #define TRUE 1 #ifndef max #define max(a,b) (((a) > (b)) ? (a) : (b)) #endif #ifndef min #define min(a,b) (((a) < (b)) ? (a) : (b)) #endif #define itoa(a,b,c) sprintf(b, "%d", a) typedef unsigned char BYTE; #endif /* END LINUX COMPILE */
(STEP #5)
In the "dlls/" directory, edit the file "h_export.cpp":
Near line 48, change this code block:
void DLLEXPORT GiveFnptrsToDll( enginefuncs_t* pengfuncsFromEngine, globalvars_t *pGlobals )
To this code block:
/* LINUX COMPILE */ #ifdef _WIN32 void DLLEXPORT GiveFnptrsToDll( enginefuncs_t* pengfuncsFromEngine, globalvars_t *pGlobals ) #else extern "C" void DLLEXPORT GiveFnptrsToDll( enginefuncs_t* pengfuncsFromEngine, globalvars_t *pGlobals ) #endif /* END LINUX COMPILE */
Also in "h_export.cpp", near line 29, change this code block:
// Required DLL entry point BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { if (fdwReason == DLL_PROCESS_ATTACH) { } else if (fdwReason == DLL_PROCESS_DETACH) { } return TRUE; }
To this code block:
/* LINUX COMPILE */ #ifdef _WIN32 // Required DLL entry point BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { if (fdwReason == DLL_PROCESS_ATTACH) { } else if (fdwReason == DLL_PROCESS_DETACH) { } return TRUE; } #endif /* END LINUX COMPILE */
(STEP #6)
In the "dlls/" directory, edit the file "plane.cpp":
Near line 15, change this code block:
#include "extdll.h" #include "plane.h"
To this code block:
#include "extdll.h" /* LINUX COMPILE */ #include "util.h" /* END LINUX COMPILE */ #include "plane.h"
(STEP #7)
In the "dlls/" directory, edit the file "util.h":
Near line 89, change this code block:
#define LINK_ENTITY_TO_CLASS(mapClassName,DLLClassName) \ extern "C" _declspec( dllexport ) void mapClassName( entvars_t *pev ); \ void mapClassName( entvars_t *pev ) { GetClassPtr( (DLLClassName *)pev ); }
To this code block:
/* LINUX COMPILE */ #define LINK_ENTITY_TO_CLASS(mapClassName,DLLClassName) \ extern "C" EXPORT void mapClassName( entvars_t *pev ); \ void mapClassName( entvars_t *pev ) { GetClassPtr( (DLLClassName *)pev ); } /* END LINUX COMPILE */
(STEP #8)
In the "dlls/" directory, edit the file "util.cpp":
Near line 1597, change this code block:
unsigned int CSaveRestoreBuffer :: HashString( const char *pszToken ) { unsigned int hash = 0; while ( *pszToken ) hash = _rotr( hash, 4 ) ^ *pszToken++; return hash; }
To this code block:
/* LINUX COMPILE */ #ifndef _WIN32 /* Thanks to Mike Harrington for this. */ extern "C" { unsigned _rotr ( unsigned val, int shift) { register unsigned lobit; /* non-zero means lo bit set */ register unsigned num = val; /* number to rotate */ shift &= 0x1f; /* modulo 32 -- this will also make negative shifts work */ while (shift--) { lobit = num & 1; /* get high bit */ num >>= 1; /* shift right one bit */ if (lobit) num |= 0x80000000; /* set hi bit if lo bit was set */ } return num; } } #endif /* END LINUX COMPILE */ unsigned int CSaveRestoreBuffer :: HashString( const char *pszToken ) { unsigned int hash = 0; while ( *pszToken ) hash = _rotr( hash, 4 ) ^ *pszToken++; return hash; }
That's all there is to editing the SDK. Now, you just need a good makefile and you should be all set. Luckily, I have one for you (revised February 5, 2000): Makefile.gz
If you have problems compiling the source, you may need to use a different version of your compiler. Check for compiler updates for your Linux distribution, or consult the GCC Home Page. EGCS-1.1.2 has been known to work very well and can be found here: GCC/EGCS Old Releases. Newer GCC releases may or may not work well, but can be found here if you are adventurous: GCC Releases.
If you have done extensive modifications to the SDK, what you see in this article might not be enough to completely port your additional work to Linux. If this happens and you need some help, feel free to contact me. I have my hands full with projects of my own, so I can't guarantee that I will spend much time on your problem (if any), but it can't hurt to ask.
I am completely open to suggestions on how to improve anything you've read in this article, including the makefile.
-Leon Hartwig
February 5, 2000
Copyright (C) 1999, 2000 Leon E. Hartwig III. All rights reserved.
Inquiries into reposting and/or distributing this article should be made to
the author.