mirror of
https://github.com/alliedmodders/metamod-source.git
synced 2024-11-28 10:24:20 +01:00
Initial sketch of new unified loader (vsp only currently).
This commit is contained in:
parent
1d9949e1a0
commit
b86f66d459
74
loader/Makefile
Normal file
74
loader/Makefile
Normal file
@ -0,0 +1,74 @@
|
||||
# (C)2004-2008 SourceMod Development Team
|
||||
# Makefile written by David "BAILOPAN" Anderson
|
||||
|
||||
#####################################
|
||||
### EDIT BELOW FOR OTHER PROJECTS ###
|
||||
#####################################
|
||||
|
||||
BINARY = server_i486.so
|
||||
|
||||
OBJECTS = loader.cpp \
|
||||
serverplugin.cpp
|
||||
|
||||
##############################################
|
||||
### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ###
|
||||
##############################################
|
||||
|
||||
C_OPT_FLAGS = -DNDEBUG -O3 -funroll-loops -pipe -fno-strict-aliasing
|
||||
C_DEBUG_FLAGS = -D_DEBUG -DDEBUG -g -ggdb3
|
||||
C_GCC4_FLAGS = -fvisibility=hidden
|
||||
CPP_GCC4_FLAGS = -fvisibility-inlines-hidden
|
||||
CPP = gcc-4.1
|
||||
|
||||
LINK += -static-libgcc
|
||||
|
||||
INCLUDE += -I. -I../core/sourcehook
|
||||
|
||||
CFLAGS += -D_LINUX -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp \
|
||||
-Dstrnicmp=strncasecmp -D_snprintf=snprintf -D_vsnprintf=vsnprintf -D_alloca=alloca \
|
||||
-Dstrcmpi=strcasecmp -Wall -Werror -Wno-uninitialized -mfpmath=sse -msse -DHAVE_STDINT_H -m32
|
||||
CPPFLAGS += -Wno-non-virtual-dtor -fno-exceptions -fno-rtti
|
||||
|
||||
################################################
|
||||
### DO NOT EDIT BELOW HERE FOR MOST PROJECTS ###
|
||||
################################################
|
||||
|
||||
ifeq "$(DEBUG)" "true"
|
||||
BIN_DIR = Debug
|
||||
CFLAGS += $(C_DEBUG_FLAGS)
|
||||
else
|
||||
BIN_DIR = Release
|
||||
CFLAGS += $(C_OPT_FLAGS)
|
||||
endif
|
||||
|
||||
GCC_VERSION := $(shell $(CPP) -dumpversion >&1 | cut -b1)
|
||||
ifeq "$(GCC_VERSION)" "4"
|
||||
CFLAGS += $(C_GCC4_FLAGS)
|
||||
CPPFLAGS += $(CPP_GCC4_FLAGS)
|
||||
endif
|
||||
|
||||
OBJ_LINUX := $(OBJECTS:%.cpp=$(BIN_DIR)/%.o)
|
||||
OBJ_LINUX := $(OBJ_LINUX:%.c=$(BIN_DIR)/%.o)
|
||||
|
||||
$(BIN_DIR)/%.o: %.cpp
|
||||
$(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<
|
||||
|
||||
$(BIN_DIR)/%.o: %.c
|
||||
$(CPP) $(INCLUDE) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
all:
|
||||
mkdir -p $(BIN_DIR)
|
||||
$(MAKE) -f Makefile metamod
|
||||
|
||||
metamod: $(OBJ_LINUX)
|
||||
$(CPP) $(INCLUDE) $(OBJ_LINUX) $(LINK) -m32 -shared -ldl -lm -o$(BIN_DIR)/$(BINARY)
|
||||
|
||||
debug:
|
||||
$(MAKE) -f Makefile all DEBUG=true
|
||||
|
||||
default: all
|
||||
|
||||
clean:
|
||||
rm -rf $(BIN_DIR)/*.o
|
||||
rm -rf $(BIN_DIR)/$(BINARY)
|
||||
|
221
loader/loader.cpp
Normal file
221
loader/loader.cpp
Normal file
@ -0,0 +1,221 @@
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "loader.h"
|
||||
#include "serverplugin.h"
|
||||
|
||||
static HMODULE mm_library = NULL;
|
||||
static char mm_fatal_logfile[PLATFORM_MAX_PATH] = "metamod-fatal.log";
|
||||
|
||||
extern void
|
||||
mm_LogFatal(const char *message, ...)
|
||||
{
|
||||
FILE *fp;
|
||||
time_t t;
|
||||
va_list ap;
|
||||
char header[256];
|
||||
|
||||
fp = fopen(mm_fatal_logfile, "at");
|
||||
if (!fp && (fp = fopen("metamod-fatal.log", "at")) == NULL)
|
||||
return;
|
||||
|
||||
t = time(NULL);
|
||||
strftime(header, sizeof(header), "%d/%d/%Y - %H:%M:%S", localtime(&t));
|
||||
fprintf(fp, "L %s: ", header);
|
||||
|
||||
va_start(ap, message);
|
||||
vfprintf(fp, message, ap);
|
||||
va_end(ap);
|
||||
|
||||
fprintf(fp, "\n");
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
static size_t
|
||||
mm_FormatArgs(char *buffer, size_t maxlength, const char *fmt, va_list params)
|
||||
{
|
||||
size_t len = vsnprintf(buffer, maxlength, fmt, params);
|
||||
|
||||
if (len >= maxlength)
|
||||
{
|
||||
len = maxlength - 1;
|
||||
buffer[len] = '\0';
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static size_t
|
||||
mm_Format(char *buffer, size_t maxlength, const char *fmt, ...)
|
||||
{
|
||||
size_t len;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
len = mm_FormatArgs(buffer, maxlength, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static bool
|
||||
mm_GetFileOfAddress(void *pAddr, char *buffer, size_t maxlength)
|
||||
{
|
||||
#if defined _WIN32
|
||||
MEMORY_BASIC_INFORMATION mem;
|
||||
if (!VirtualQuery(pAddr, &mem, sizeof(mem)))
|
||||
return false;
|
||||
if (mem.AllocationBase == NULL)
|
||||
return false;
|
||||
HMODULE dll = (HMODULE)mem.AllocationBase;
|
||||
GetModuleFileName(dll, (LPTSTR)buffer, maxlength);
|
||||
#else
|
||||
Dl_info info;
|
||||
if (!dladdr(pAddr, &info))
|
||||
return false;
|
||||
if (!info.dli_fbase || !info.dli_fname)
|
||||
return false;
|
||||
const char *dllpath = info.dli_fname;
|
||||
snprintf(buffer, maxlength, "%s", dllpath);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char *backend_names[3] =
|
||||
{
|
||||
"1.ep1",
|
||||
"2.ep2",
|
||||
"2.l4d"
|
||||
};
|
||||
|
||||
#if defined _WIN32
|
||||
#define LIBRARY_EXT ".dll"
|
||||
#define LIBRARY_MINEXT ".dll"
|
||||
#elif defined __linux__
|
||||
#define LIBRARY_EXT "_i486.so"
|
||||
#define LIBRARY_MINEXT ".so"
|
||||
#endif
|
||||
|
||||
#if defined _WIN32
|
||||
static void
|
||||
mm_GetPlatformError(char *buffer, size_t maxlength)
|
||||
{
|
||||
DWORD dw = GetLastError();
|
||||
FormatMessageA(
|
||||
FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
dw,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPSTR)buffer,
|
||||
maxlength,
|
||||
NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
mm_LoadMetamodLibrary(MetamodBackend backend, char *buffer, size_t maxlength)
|
||||
{
|
||||
size_t len, temp_len;
|
||||
char mm_path[PLATFORM_MAX_PATH * 2];
|
||||
|
||||
/* Get our path */
|
||||
if (!mm_GetFileOfAddress((void*)mm_GetFileOfAddress, mm_path, sizeof(mm_path)))
|
||||
return false;
|
||||
|
||||
len = strlen(mm_path);
|
||||
temp_len = strlen("server" LIBRARY_EXT);
|
||||
if (len < temp_len)
|
||||
return false;
|
||||
|
||||
/* Build log file name */
|
||||
mm_path[len - temp_len] = '\0';
|
||||
mm_Format(mm_fatal_logfile,
|
||||
sizeof(mm_fatal_logfile),
|
||||
"%smetamod-fatal.log",
|
||||
mm_path);
|
||||
|
||||
/* Replace server.dll with the new binary we want */
|
||||
mm_Format(&mm_path[len - temp_len],
|
||||
sizeof(mm_path) - (len - temp_len),
|
||||
"metamod.%s" LIBRARY_MINEXT,
|
||||
backend_names[backend]);
|
||||
|
||||
#if defined _WIN32
|
||||
mm_library = LoadLibrary(mm_path);
|
||||
|
||||
if (mm_library == NULL)
|
||||
{
|
||||
mm_GetPlatformError(buffer, maxlength);
|
||||
return false;
|
||||
}
|
||||
#elif defined __linux__
|
||||
mm_library = dlopen(mm_path, RTLD_NOW);
|
||||
|
||||
if (mm_library == NULL)
|
||||
{
|
||||
mm_Format(buffer, maxlength, "%s", dlerror());
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
mm_UnloadMetamodLibrary()
|
||||
{
|
||||
#if defined _WIN32
|
||||
FreeLibrary(mm_library);
|
||||
#else
|
||||
dlclose(mm_library);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined _WIN32
|
||||
#define EXPORT extern "C" __declspec(dllexport)
|
||||
#elif defined __GNUC__
|
||||
#if __GNUC__ == 4
|
||||
#define EXPORT extern "C" __attribute__ ((visibility("default")))
|
||||
#else
|
||||
#define EXPORT extern "C"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
EXPORT void *
|
||||
CreateInterface(const char *name, int *ret)
|
||||
{
|
||||
if (mm_library != NULL)
|
||||
{
|
||||
if (ret != NULL)
|
||||
*ret = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *ptr;
|
||||
if (strncmp(name, "ISERVERPLUGINCALLBACKS", 22) == 0)
|
||||
{
|
||||
ptr = mm_GetVspCallbacks(atoi(&name[22]));
|
||||
if (ret != NULL)
|
||||
*ret = ptr != NULL ? 0 : 1;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
if (ret != NULL)
|
||||
*ret = 1;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
extern void *
|
||||
mm_GetProcAddress(const char *name)
|
||||
{
|
||||
#if defined _WIN32
|
||||
return GetProcAddress(mm_library, name);
|
||||
#elif defined __linux__
|
||||
return dlsym(mm_library, name);
|
||||
#endif
|
||||
}
|
||||
|
48
loader/loader.h
Normal file
48
loader/loader.h
Normal file
@ -0,0 +1,48 @@
|
||||
#ifndef _INCLUDE_METAMOD_SOURCE_LOADER_H_
|
||||
#define _INCLUDE_METAMOD_SOURCE_LOADER_H_
|
||||
|
||||
#define SH_COMP_GCC 1
|
||||
#define SH_COMP_MSVC 2
|
||||
|
||||
#if defined WIN32
|
||||
#define WINDOWS_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#define PLATFORM_MAX_PATH MAX_PATH
|
||||
typedef INTPTR intptr_t;
|
||||
typedef UINTPTR uintptr_t;
|
||||
#define SH_COMP SH_COMP_MSVC
|
||||
#elif defined __linux__
|
||||
#include <dlfcn.h>
|
||||
#include <dirent.h>
|
||||
#include <stdint.h>
|
||||
typedef void * HMODULE;
|
||||
#define PLATFORM_MAX_PATH PATH_MAX
|
||||
#define SH_COMP SH_COMP_GCC
|
||||
#else
|
||||
#error "OS detection failed"
|
||||
#endif
|
||||
|
||||
#define SH_PTRSIZE sizeof(void*)
|
||||
|
||||
enum MetamodBackend
|
||||
{
|
||||
MMBackend_Episode1 = 0,
|
||||
MMBackend_Episode2,
|
||||
MMBackend_Left4Dead,
|
||||
MMBackend_UNKNOWN
|
||||
};
|
||||
|
||||
extern bool
|
||||
mm_LoadMetamodLibrary(MetamodBackend backend, char *buffer, size_t maxlength);
|
||||
|
||||
extern void *
|
||||
mm_GetProcAddress(const char *name);
|
||||
|
||||
extern void
|
||||
mm_UnloadMetamodLibrary();
|
||||
|
||||
extern void
|
||||
mm_LogFatal(const char *message, ...);
|
||||
|
||||
#endif /* _INCLUDE_METAMOD_SOURCE_LOADER_H_ */
|
||||
|
24
loader/loader_bridge.h
Normal file
24
loader/loader_bridge.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef _INCLUDE_METAMOD_SOURCE_LOADER_BRIDGE_H_
|
||||
#define _INCLUDE_METAMOD_SOURCE_LOADER_BRIDGE_H_
|
||||
|
||||
typedef void* (*QueryValveInterface)(const char *pName, int *pReturnCode);
|
||||
class IServerPluginCallbacks;
|
||||
|
||||
struct vsp_bridge_info
|
||||
{
|
||||
QueryValveInterface engineFactory;
|
||||
QueryValveInterface gsFactory;
|
||||
IServerPluginCallbacks * vsp_callbacks;
|
||||
unsigned int vsp_version;
|
||||
};
|
||||
|
||||
class IVspBridge
|
||||
{
|
||||
public:
|
||||
virtual bool Load(const vsp_bridge_info *info) = 0;
|
||||
virtual void Unload() = 0;
|
||||
virtual const char *GetDescription() = 0;
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE_METAMOD_SOURCE_LOADER_BRIDGE_H_ */
|
||||
|
248
loader/serverplugin.cpp
Normal file
248
loader/serverplugin.cpp
Normal file
@ -0,0 +1,248 @@
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include "loader.h"
|
||||
#include <sh_memfuncinfo.h>
|
||||
#include <sh_memory.h>
|
||||
#include "serverplugin.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PLUGIN_CONTINUE = 0,
|
||||
PLUGIN_OVERRIDE,
|
||||
PLUGIN_STOP,
|
||||
} PLUGIN_RESULT;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
eQueryCvarValueStatus_ValueIntact=0,
|
||||
eQueryCvarValueStatus_CvarNotFound=1,
|
||||
eQueryCvarValueStatus_NotACvar=2,
|
||||
eQueryCvarValueStatus_CvarProtected=3
|
||||
} EQueryCvarValueStatus;
|
||||
|
||||
typedef int QueryCvarCookie_t;
|
||||
class CCommand;
|
||||
class IServerPluginCallbacks;
|
||||
struct edict_t;
|
||||
|
||||
#if defined WIN32
|
||||
#define LIBRARY_EXT ".dll"
|
||||
#else
|
||||
#define LIBRARY_EXT "_i486.so"
|
||||
#endif
|
||||
|
||||
class IRandomThings
|
||||
{
|
||||
public:
|
||||
virtual PLUGIN_RESULT ClientCommand(edict_t *pEntity, const CCommand& args)
|
||||
{
|
||||
return PLUGIN_CONTINUE;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The vtable must match the general layout for ISPC. We modify the vtable
|
||||
* based on what we get back.
|
||||
*/
|
||||
class ServerPlugin
|
||||
{
|
||||
IVspBridge* bridge;
|
||||
unsigned int vsp_version;
|
||||
bool load_allowed;
|
||||
public:
|
||||
ServerPlugin()
|
||||
{
|
||||
load_allowed = false;
|
||||
}
|
||||
virtual bool Load(QueryValveInterface engineFactory, QueryValveInterface gsFactory)
|
||||
{
|
||||
MetamodBackend backend = MMBackend_UNKNOWN;
|
||||
|
||||
if (!load_allowed)
|
||||
return false;
|
||||
|
||||
load_allowed = false;
|
||||
|
||||
/* Check for L4D */
|
||||
if (engineFactory("VEngineServer022", NULL) != NULL &&
|
||||
engineFactory("VEngineCvar007", NULL) != NULL)
|
||||
{
|
||||
backend = MMBackend_Left4Dead;
|
||||
}
|
||||
else if (engineFactory("VEngineServer021", NULL) != NULL)
|
||||
{
|
||||
/* Check for OB */
|
||||
if (engineFactory("VEngineCvar004", NULL) != NULL &&
|
||||
engineFactory("VModelInfoServer002", NULL) != NULL)
|
||||
{
|
||||
backend = MMBackend_Episode2;
|
||||
}
|
||||
/* Check for EP1 */
|
||||
else if (engineFactory("VModelInfoServer001", NULL) != NULL &&
|
||||
(engineFactory("VEngineCvar003", NULL) != NULL ||
|
||||
engineFactory("VEngineCvar002", NULL) != NULL))
|
||||
{
|
||||
backend = MMBackend_Episode1;
|
||||
}
|
||||
}
|
||||
|
||||
if (backend == MMBackend_UNKNOWN)
|
||||
{
|
||||
mm_LogFatal("Could not detect engine version, spewing stats:");
|
||||
return false;
|
||||
}
|
||||
else if (backend >= MMBackend_Episode2)
|
||||
{
|
||||
/* We need to insert the right type of call into this vtable */
|
||||
void **vtable_src;
|
||||
void **vtable_dest;
|
||||
IRandomThings sample;
|
||||
SourceHook::MemFuncInfo mfp_dest, mfp_src;
|
||||
|
||||
mfp_dest.isVirtual = false;
|
||||
mfp_src.isVirtual = false;
|
||||
|
||||
SourceHook::GetFuncInfo(&ServerPlugin::ClientCommand, mfp_dest);
|
||||
SourceHook::GetFuncInfo(&IRandomThings::ClientCommand, mfp_src);
|
||||
|
||||
assert(mfp_dest.isVirtual);
|
||||
assert(mfp_dest.thisptroffs == 0);
|
||||
assert(mfp_dest.vtbloffs == 0);
|
||||
assert(mfp_src.isVirtual);
|
||||
assert(mfp_src.thisptroffs == 0);
|
||||
assert(mfp_src.vtbloffs == 0);
|
||||
|
||||
vtable_src = (void **)*(void **)&sample;
|
||||
vtable_dest = (void **)*(void **)this;
|
||||
SourceHook::SetMemAccess(&vtable_dest[mfp_dest.vtblindex],
|
||||
sizeof(void*),
|
||||
SH_MEM_READ|SH_MEM_WRITE|SH_MEM_EXEC);
|
||||
vtable_dest[mfp_dest.vtblindex] = vtable_src[mfp_src.vtblindex];
|
||||
}
|
||||
|
||||
char error[255];
|
||||
if (!mm_LoadMetamodLibrary(backend, error, sizeof(error)))
|
||||
{
|
||||
mm_LogFatal("Detected engine %d but could not load: %s", backend, error);
|
||||
return false;
|
||||
}
|
||||
|
||||
typedef IVspBridge *(*GetVspBridge)();
|
||||
GetVspBridge get_bridge = (GetVspBridge)mm_GetProcAddress("GetVspBridge");
|
||||
if (get_bridge == NULL)
|
||||
{
|
||||
mm_UnloadMetamodLibrary();
|
||||
mm_LogFatal("Detected engine %d but could not find GetVspBridge callback.");
|
||||
return false;
|
||||
}
|
||||
|
||||
bridge = get_bridge();
|
||||
|
||||
vsp_bridge_info info;
|
||||
|
||||
info.engineFactory = engineFactory;
|
||||
info.gsFactory = gsFactory;
|
||||
info.vsp_callbacks = (IServerPluginCallbacks*)this;
|
||||
info.vsp_version = vsp_version;
|
||||
|
||||
if (!bridge->Load(&info))
|
||||
{
|
||||
mm_UnloadMetamodLibrary();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
virtual void Unload()
|
||||
{
|
||||
if (bridge == NULL)
|
||||
return;
|
||||
bridge->Unload();
|
||||
}
|
||||
virtual void Pause()
|
||||
{
|
||||
}
|
||||
virtual void UnPause()
|
||||
{
|
||||
}
|
||||
virtual const char *GetPluginDescription()
|
||||
{
|
||||
if (bridge == NULL)
|
||||
return "Metamod:Source Loader Shim";
|
||||
return bridge->GetDescription();
|
||||
}
|
||||
virtual void LevelInit(char const *pMapName)
|
||||
{
|
||||
}
|
||||
virtual void ServerActivate(edict_t *pEdictList, int edictCount, int clientMax)
|
||||
{
|
||||
}
|
||||
virtual void GameFrame(bool simulating)
|
||||
{
|
||||
}
|
||||
virtual void LevelShutdown()
|
||||
{
|
||||
}
|
||||
virtual void ClientActive(edict_t *pEntity)
|
||||
{
|
||||
}
|
||||
virtual void ClientDisconnect(edict_t *pEntity)
|
||||
{
|
||||
}
|
||||
virtual void ClientPutInServer(edict_t *pEntity, char const *playername)
|
||||
{
|
||||
}
|
||||
virtual void SetCommandClient(int index)
|
||||
{
|
||||
}
|
||||
virtual void ClientSettingsChanged(edict_t *pEdict)
|
||||
{
|
||||
}
|
||||
virtual PLUGIN_RESULT ClientConnect(bool *bAllowConnect,
|
||||
edict_t *pEntity,
|
||||
const char *pszName,
|
||||
const char *pszAddress,
|
||||
char *reject,
|
||||
int maxrejectlen)
|
||||
{
|
||||
return PLUGIN_CONTINUE;
|
||||
}
|
||||
virtual PLUGIN_RESULT ClientCommand(edict_t *pEntity)
|
||||
{
|
||||
return PLUGIN_CONTINUE;
|
||||
}
|
||||
virtual PLUGIN_RESULT NetworkIDValidated(const char *pszUserName, const char *pszNetworkID)
|
||||
{
|
||||
return PLUGIN_CONTINUE;
|
||||
}
|
||||
virtual void OnQueryCvarValueFinished(QueryCvarCookie_t iCookie,
|
||||
edict_t *pPlayerEntity,
|
||||
EQueryCvarValueStatus eStatus,
|
||||
const char *pCvarName,
|
||||
const char *pCvarValue)
|
||||
{
|
||||
}
|
||||
void PrepForLoad(unsigned int version)
|
||||
{
|
||||
vsp_version = version;
|
||||
load_allowed = true;
|
||||
}
|
||||
bool IsLoaded()
|
||||
{
|
||||
return bridge != NULL;
|
||||
}
|
||||
};
|
||||
|
||||
ServerPlugin mm_vsp_callbacks;
|
||||
|
||||
void *mm_GetVspCallbacks(unsigned int version)
|
||||
{
|
||||
if (mm_vsp_callbacks.IsLoaded())
|
||||
return NULL;
|
||||
|
||||
mm_vsp_callbacks.PrepForLoad(version);
|
||||
|
||||
return &mm_vsp_callbacks;
|
||||
}
|
||||
|
10
loader/serverplugin.h
Normal file
10
loader/serverplugin.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef _INCLUDE_METAMOD_SOURCE_SERVERPLUGINS_H_
|
||||
#define _INCLUDE_METAMOD_SOURCE_SERVERPLUGINS_H_
|
||||
|
||||
#include "loader_bridge.h"
|
||||
|
||||
extern void *
|
||||
mm_GetVspCallbacks(unsigned int version);
|
||||
|
||||
#endif /* _INCLUDE_METAMOD_SOURCE_SERVERPLUGINS_H_ */
|
||||
|
Loading…
Reference in New Issue
Block a user