#include #include #include #include #include "loader.h" #include #include #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; } }; IVspBridge *vsp_bridge = NULL; /** * The vtable must match the general layout for ISPC. We modify the vtable * based on what we get back. */ class ServerPlugin { unsigned int vsp_version; bool load_allowed; public: ServerPlugin() { load_allowed = false; } virtual bool Load(QueryValveInterface engineFactory, QueryValveInterface gsFactory) { if (!load_allowed) return false; load_allowed = false; MetamodBackend backend = mm_DetermineBackend(engineFactory); if (backend == MMBackend_UNKNOWN) { mm_LogFatal("Could not detect engine version"); 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", backend); return false; } vsp_bridge = get_bridge(); vsp_bridge_info info; info.engineFactory = engineFactory; info.gsFactory = gsFactory; info.vsp_callbacks = (IServerPluginCallbacks*)this; info.vsp_version = vsp_version; strcpy(error, "Unknown error"); if (!vsp_bridge->Load(&info, error, sizeof(error))) { vsp_bridge = NULL; mm_UnloadMetamodLibrary(); mm_LogFatal("Unknown error loading Metamod for engine %d: %s", backend, error); return false; } return true; } virtual void Unload() { if (vsp_bridge == NULL) return; vsp_bridge->Unload(); mm_UnloadMetamodLibrary(); } virtual void Pause() { } virtual void UnPause() { } virtual const char *GetPluginDescription() { if (vsp_bridge == NULL) return "Metamod:Source Loader Shim"; return vsp_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; } }; ServerPlugin mm_vsp_callbacks; void *mm_GetVspCallbacks(unsigned int version) { if (vsp_bridge != NULL) return NULL; /* Only support versions 1 or 2 right now */ if (version > 2) return NULL; mm_vsp_callbacks.PrepForLoad(version); return &mm_vsp_callbacks; }