mirror of
https://github.com/alliedmodders/metamod-source.git
synced 2025-01-19 08:52:34 +01:00
Initial import of new GameDLL loading code
--HG-- extra : convert_revision : svn%3Ac2935e3e-5518-0410-8daf-afa5dab7d4e3/trunk%40123
This commit is contained in:
parent
36dda69d9d
commit
67039543cd
@ -1,41 +0,0 @@
|
||||
/* ======== SourceMM ========
|
||||
* Copyright (C) 2004-2005 Metamod:Source Development Team
|
||||
* No warranties of any kind
|
||||
*
|
||||
* License: zlib/libpng
|
||||
*
|
||||
* Author(s): David "BAILOPAN" Anderson
|
||||
* ============================
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_HLTVDIRECTOR_H
|
||||
#define _INCLUDE_HLTVDIRECTOR_H
|
||||
|
||||
/**
|
||||
* @brief Dummy class for IHLTVDirector
|
||||
* @file CHLTVDirector.
|
||||
*/
|
||||
|
||||
#include <eiface.h>
|
||||
#include <ihltvdirector.h>
|
||||
|
||||
class CHLTVDirector : IHLTVDirector
|
||||
{
|
||||
IHLTVDirector *m_pOrig;
|
||||
public:
|
||||
CHLTVDirector() : m_pOrig(0) {}
|
||||
void SetOrig(IHLTVDirector *pOrig)
|
||||
{
|
||||
m_pOrig = pOrig;
|
||||
}
|
||||
virtual bool IsActive() { return m_pOrig->IsActive(); }
|
||||
virtual void SetHLTVServer(IHLTVServer *hltv) { m_pOrig->SetHLTVServer(hltv); }
|
||||
virtual IHLTVServer *GetHLTVServer() { return m_pOrig->GetHLTVServer(); }
|
||||
virtual int GetDirectorTick() { return m_pOrig->GetDirectorTick(); }
|
||||
virtual int GetPVSEntity() { return m_pOrig->GetPVSEntity(); }
|
||||
virtual Vector GetPVSOrigin() { return m_pOrig->GetPVSOrigin(); }
|
||||
virtual float GetDelay() { return m_pOrig->GetDelay(); }
|
||||
virtual const char**GetModEvents() { return m_pOrig->GetModEvents(); }
|
||||
};
|
||||
|
||||
#endif //_INCLUDE_HLTVDIRECTOR_H
|
@ -260,7 +260,7 @@ CPluginManager::CPlugin *CPluginManager::_Load(const char *file, PluginId source
|
||||
snprintf(error, maxlen, "Plugin API %d is newer than internal version (%d)", api, PLAPI_VERSION);
|
||||
pl->m_Status = Pl_Error;
|
||||
} else {
|
||||
if (pl->m_API->Load(pl->m_Id, static_cast<ISmmAPI *>(&g_SmmAPI), &(pl->fac_list), error, maxlen))
|
||||
if (pl->m_API->Load(pl->m_Id, static_cast<ISmmAPI *>(&g_SmmAPI), &(pl->fac_list), error, maxlen, m_AllLoaded))
|
||||
{
|
||||
pl->m_Status = Pl_Running;
|
||||
if (m_AllLoaded)
|
||||
@ -424,6 +424,20 @@ bool CPluginManager::Query(PluginId id, const char *&file, factories *&list, Pl_
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPluginManager::QueryRunning(PluginId id, char *error, size_t maxlength)
|
||||
{
|
||||
CPlugin *pl = FindById(id);
|
||||
|
||||
if (!pl || !pl->m_API)
|
||||
{
|
||||
if (error)
|
||||
snprintf(error, maxlength, "Plugin not valid");
|
||||
return false;
|
||||
}
|
||||
|
||||
return pl->m_API->QueryRunning(error, maxlength);
|
||||
}
|
||||
|
||||
PluginIter CPluginManager::_begin()
|
||||
{
|
||||
return m_Plugins.begin();
|
||||
|
@ -33,6 +33,8 @@
|
||||
* 5: Bumped version for SourceHook V4 (2005-05-01)
|
||||
* 6: Added functions for console printing (2005-05-26)
|
||||
* MC 7: Changed template libraries (2005-08-11)
|
||||
* New loading structure mechanism
|
||||
* New SourceHook version
|
||||
*/
|
||||
|
||||
#define PLAPI_MIN_VERSION 7
|
||||
@ -75,6 +77,7 @@ namespace SourceMM
|
||||
bool UnloadAll();
|
||||
public:
|
||||
bool Query(PluginId id, const char *&file, factories *&list, Pl_Status &status, PluginId &source);
|
||||
bool QueryRunning(PluginId id, char *error, size_t maxlength);
|
||||
|
||||
void AddPluginCvar(ISmmPlugin *api, ConCommandBase *pCvar);
|
||||
void AddPluginCmd(ISmmPlugin *api, ConCommandBase *pCmd);
|
||||
|
@ -1,77 +0,0 @@
|
||||
/* ======== SourceMM ========
|
||||
* Copyright (C) 2004-2005 Metamod:Source Development Team
|
||||
* No warranties of any kind
|
||||
*
|
||||
* License: zlib/libpng
|
||||
*
|
||||
* Author(s): David "BAILOPAN" Anderson
|
||||
* ============================
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_CSERVERGAMECLIENTS_H
|
||||
#define _INCLUDE_CSERVERGAMECLIENTS_H
|
||||
|
||||
/**
|
||||
* @brief Dummy class for IServerGameClients
|
||||
* @file CServerGameClients.h
|
||||
*/
|
||||
|
||||
#include <eiface.h>
|
||||
|
||||
class CServerGameClients : public IServerGameClients
|
||||
{
|
||||
IServerGameClients *m_pOrig;
|
||||
public:
|
||||
CServerGameClients() : m_pOrig(0) {}
|
||||
void SetOrig(IServerGameClients *pOrig)
|
||||
{
|
||||
m_pOrig = pOrig;
|
||||
}
|
||||
|
||||
virtual void GetPlayerLimits(int& minplayers, int& maxplayers, int &defaultMaxPlayers) const
|
||||
{ m_pOrig->GetPlayerLimits(minplayers, maxplayers, defaultMaxPlayers); }
|
||||
|
||||
virtual bool ClientConnect(edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen)
|
||||
{ return m_pOrig->ClientConnect(pEntity, pszName, pszAddress, reject, maxrejectlen); }
|
||||
|
||||
virtual void ClientActive(edict_t *pEntity, bool bLoadGame)
|
||||
{ m_pOrig->ClientActive(pEntity, bLoadGame); }
|
||||
|
||||
virtual void ClientDisconnect(edict_t *pEntity)
|
||||
{ m_pOrig->ClientDisconnect(pEntity); }
|
||||
|
||||
virtual void ClientPutInServer(edict_t *pEntity, char const *playername)
|
||||
{ m_pOrig->ClientPutInServer(pEntity, playername); }
|
||||
|
||||
virtual void ClientCommand(edict_t *pEntity)
|
||||
{ m_pOrig->ClientCommand(pEntity); }
|
||||
|
||||
virtual void SetCommandClient(int index)
|
||||
{ m_pOrig->SetCommandClient(index); }
|
||||
|
||||
virtual void ClientSettingsChanged(edict_t *pEdict)
|
||||
{ m_pOrig->ClientSettingsChanged(pEdict); }
|
||||
|
||||
virtual void ClientSetupVisibility(edict_t *pViewEntity, edict_t *pClient, unsigned char *pvs, int pvssize)
|
||||
{ m_pOrig->ClientSetupVisibility(pViewEntity, pClient, pvs, pvssize); }
|
||||
|
||||
virtual float ProcessUsercmds(edict_t *player, bf_read *buf, int numcmds, int totalcmds, int dropped_packets, bool ignore, bool paused)
|
||||
{ return m_pOrig->ProcessUsercmds(player, buf, numcmds, totalcmds, dropped_packets, ignore, paused); }
|
||||
|
||||
virtual void PostClientMessagesSent()
|
||||
{ m_pOrig->PostClientMessagesSent(); }
|
||||
|
||||
virtual CPlayerState *GetPlayerState(edict_t *player)
|
||||
{ return m_pOrig->GetPlayerState(player); }
|
||||
|
||||
virtual void ClientEarPosition(edict_t *pEntity, Vector *pEarOrigin)
|
||||
{ m_pOrig->ClientEarPosition(pEntity, pEarOrigin); }
|
||||
|
||||
virtual int GetReplayDelay(edict_t *player)
|
||||
{ return m_pOrig->GetReplayDelay(player); }
|
||||
|
||||
virtual void GetBugReportInfo(char *buf, int buflen)
|
||||
{ m_pOrig->GetBugReportInfo(buf, buflen); }
|
||||
};
|
||||
|
||||
#endif //_INCLUDE_CSERVERGAMECLIENTS_H
|
@ -1,95 +0,0 @@
|
||||
/* ======== SourceMM ========
|
||||
* Copyright (C) 2004-2005 Metamod:Source Development Team
|
||||
* No warranties of any kind
|
||||
*
|
||||
* License: zlib/libpng
|
||||
*
|
||||
* Author(s): David "BAILOPAN" Anderson
|
||||
* ============================
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_CSERVER_GAMEDLL_H
|
||||
#define _INCLUDE_CSERVER_GAMEDLL_H
|
||||
|
||||
/**
|
||||
* @brief Defines wrapper class for gamedll interfaces
|
||||
* @file CServerGameDLL.h
|
||||
*/
|
||||
|
||||
#include <eiface.h>
|
||||
#include "oslink.h"
|
||||
|
||||
/**
|
||||
* @brief Empty class. We only care about DLLInit.
|
||||
*/
|
||||
class CServerGameDLL : public IServerGameDLL
|
||||
{
|
||||
IServerGameDLL *m_pOrig;
|
||||
char m_GameDescBuffer[256];
|
||||
public:
|
||||
CServerGameDLL() : m_pOrig(0)
|
||||
{
|
||||
strcpy(m_GameDescBuffer, "Metamod:Source");
|
||||
}
|
||||
void SetOrig(IServerGameDLL *pOrig)
|
||||
{
|
||||
m_pOrig = pOrig;
|
||||
snprintf(m_GameDescBuffer, 255, "%s", pOrig->GetGameDescription());
|
||||
}
|
||||
|
||||
virtual bool DLLInit( CreateInterfaceFn engineFactory, CreateInterfaceFn physicsFactory, CreateInterfaceFn fileSystemFactory, CGlobalVars *pGlobals);
|
||||
virtual bool GameInit( void )
|
||||
{ return m_pOrig->GameInit(); }
|
||||
virtual bool LevelInit( char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background );
|
||||
virtual void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
|
||||
{ m_pOrig->ServerActivate(pEdictList, edictCount, clientMax); }
|
||||
virtual void GameFrame( bool simulating )
|
||||
{ m_pOrig->GameFrame(simulating); }
|
||||
virtual void PreClientUpdate( bool simulating )
|
||||
{ m_pOrig->PreClientUpdate(simulating); }
|
||||
virtual void LevelShutdown( void );
|
||||
virtual void GameShutdown( void )
|
||||
{ m_pOrig->GameShutdown(); }
|
||||
virtual void DLLShutdown( void );
|
||||
virtual float GetTickInterval( void ) const
|
||||
{ return m_pOrig->GetTickInterval(); }
|
||||
virtual ServerClass *GetAllServerClasses( void )
|
||||
{ return m_pOrig->GetAllServerClasses(); }
|
||||
virtual const char *GetGameDescription( void );
|
||||
virtual void CreateNetworkStringTables( void )
|
||||
{ m_pOrig->CreateNetworkStringTables(); }
|
||||
virtual CSaveRestoreData *SaveInit( int size )
|
||||
{ return m_pOrig->SaveInit(size); }
|
||||
virtual void SaveWriteFields( CSaveRestoreData *a1, const char *a2, void *a3, datamap_t *a4, typedescription_t *a5, int a6)
|
||||
{ m_pOrig->SaveWriteFields(a1, a2, a3, a4, a5, a6); }
|
||||
virtual void SaveReadFields( CSaveRestoreData *a1, const char *a2, void *a3, datamap_t *a4, typedescription_t *a5, int a6)
|
||||
{ m_pOrig->SaveReadFields(a1, a2, a3, a4, a5, a6); }
|
||||
virtual void SaveGlobalState( CSaveRestoreData *a1)
|
||||
{ m_pOrig->SaveGlobalState(a1); }
|
||||
virtual void RestoreGlobalState( CSaveRestoreData *a1)
|
||||
{ m_pOrig->RestoreGlobalState(a1); }
|
||||
virtual void PreSave( CSaveRestoreData *a1)
|
||||
{ m_pOrig->PreSave(a1); }
|
||||
virtual void Save( CSaveRestoreData *a1)
|
||||
{ m_pOrig->Save(a1); }
|
||||
virtual void GetSaveComment( char *comment, int maxlength )
|
||||
{ m_pOrig->GetSaveComment(comment, maxlength); }
|
||||
virtual void WriteSaveHeaders( CSaveRestoreData *a1)
|
||||
{ m_pOrig->WriteSaveHeaders(a1); }
|
||||
virtual void ReadRestoreHeaders( CSaveRestoreData *a1)
|
||||
{ m_pOrig->ReadRestoreHeaders(a1); }
|
||||
virtual void Restore( CSaveRestoreData *a1, bool a2)
|
||||
{ m_pOrig->Restore(a1, a2); }
|
||||
virtual bool IsRestoring()
|
||||
{ return m_pOrig->IsRestoring(); }
|
||||
virtual int CreateEntityTransitionList( CSaveRestoreData *a1, int a2)
|
||||
{ return m_pOrig->CreateEntityTransitionList(a1, a2); }
|
||||
virtual void BuildAdjacentMapList( void )
|
||||
{ m_pOrig->BuildAdjacentMapList(); }
|
||||
virtual bool GetUserMessageInfo( int msg_type, char *name, int maxnamelength, int& size )
|
||||
{ return m_pOrig->GetUserMessageInfo(msg_type, name, maxnamelength, size); }
|
||||
virtual CStandardSendProxies* GetStandardSendProxies()
|
||||
{ return m_pOrig->GetStandardSendProxies(); }
|
||||
};
|
||||
|
||||
#endif //_INCLUDE_CSERVER_GAMEDLL_H
|
@ -1,45 +0,0 @@
|
||||
/* ======== SourceMM ========
|
||||
* Copyright (C) 2004-2005 Metamod:Source Development Team
|
||||
* No warranties of any kind
|
||||
*
|
||||
* License: zlib/libpng
|
||||
*
|
||||
* Author(s): David "BAILOPAN" Anderson
|
||||
* ============================
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_CSERVER_GAMEENTS_H
|
||||
#define _INCLUDE_CSERVER_GAMEENTS_H
|
||||
|
||||
/**
|
||||
* @brief Dummy class for IServerGameEnts
|
||||
* @file CServerGameEnts.h
|
||||
*/
|
||||
|
||||
#include <eiface.h>
|
||||
|
||||
class CServerGameEnts : public IServerGameEnts
|
||||
{
|
||||
IServerGameEnts *m_pOrig;
|
||||
public:
|
||||
CServerGameEnts() : m_pOrig(0) {}
|
||||
void SetOrig(IServerGameEnts *pOrig)
|
||||
{
|
||||
m_pOrig = pOrig;
|
||||
}
|
||||
|
||||
virtual void SetDebugEdictBase(edict_t *base)
|
||||
{ m_pOrig->SetDebugEdictBase(base); }
|
||||
virtual void MarkEntitiesAsTouching(edict_t *e1, edict_t *e2)
|
||||
{ m_pOrig->MarkEntitiesAsTouching(e1, e2); }
|
||||
virtual void FreeContainingEntity(edict_t *e)
|
||||
{ m_pOrig->FreeContainingEntity(e); }
|
||||
virtual edict_t *BaseEntityToEdict(CBaseEntity *pEnt)
|
||||
{ return m_pOrig->BaseEntityToEdict(pEnt); }
|
||||
virtual CBaseEntity *EdictToBaseEntity(edict_t *pEdict)
|
||||
{ return m_pOrig->EdictToBaseEntity(pEdict); }
|
||||
virtual void CheckTransmit(CCheckTransmitInfo *pInfo, const unsigned short *pEdictIndices, int nEdicts)
|
||||
{ m_pOrig->CheckTransmit(pInfo, pEdictIndices, nEdicts); }
|
||||
};
|
||||
|
||||
#endif //_INCLUDE_CSERVER_GAMEENTS_H
|
@ -18,6 +18,17 @@
|
||||
|
||||
#include "ISmmPlugin.h"
|
||||
|
||||
/**
|
||||
* @brief Load sources
|
||||
*/
|
||||
enum
|
||||
{
|
||||
Pl_BadLoad=0,
|
||||
Pl_Console=-1,
|
||||
Pl_File=-2,
|
||||
Pl_MinId=1,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Status of a plugin at runtime
|
||||
*/
|
||||
@ -30,17 +41,6 @@ enum Pl_Status
|
||||
Pl_Running=0,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Load sources
|
||||
*/
|
||||
enum
|
||||
{
|
||||
Pl_BadLoad=0,
|
||||
Pl_Console=-1,
|
||||
Pl_File=-2,
|
||||
Pl_MinId=1,
|
||||
};
|
||||
|
||||
typedef int PluginId;
|
||||
struct factories;
|
||||
|
||||
@ -48,7 +48,9 @@ class ISmmPluginManager
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Loads a plugin and returns its id
|
||||
* @brief Loads a plugin and returns its id. If this is called before DLLInit(),
|
||||
* then the plugin is considered to be "hot" - it might refuse its own load later!
|
||||
* Also, a hot plugin might not have an error message.
|
||||
*
|
||||
* @param file String containing file name
|
||||
* @param source Specifies who loaded the plugin
|
||||
@ -109,6 +111,16 @@ public:
|
||||
* @return True on success, false if not found
|
||||
*/
|
||||
virtual bool Query(PluginId id, const char *&file, factories *&list, Pl_Status &status, PluginId &source) =0;
|
||||
|
||||
/**
|
||||
* @brief Checks another plugin's QueryRunning() status.
|
||||
*
|
||||
* @param id Id of plugin
|
||||
* @param error Message buffer
|
||||
* @param maxlen Size of error buffer
|
||||
* @return Status value
|
||||
*/
|
||||
virtual bool QueryRunning(PluginId id, char *error, size_t maxlength) =0;
|
||||
};
|
||||
|
||||
#endif //_INCLUDE_PLUGINMANAGER_H
|
||||
|
@ -38,42 +38,83 @@ class ISmmPlugin
|
||||
{
|
||||
public:
|
||||
virtual int GetApiVersion() { return PLAPI_VERSION; }
|
||||
virtual ~ISmmPlugin() { }
|
||||
public:
|
||||
/**
|
||||
* @brief Called on plugin load.
|
||||
* NOTE - As of API 7, this is called as DLLInit() executes - after the parameters are known, but before
|
||||
* the original GameDLL function is called. Therefore, you cannot hook it, but you don't need to -
|
||||
* Load() is basically your hook.
|
||||
* NOTE - As of API 7, you can override factories before the engine and gamedll exchange them.
|
||||
* However, take care to note that if your plugin is unloaded, and the gamedll/engine have cached
|
||||
* an interface you've passed, something will definitely crash. Be careful.
|
||||
*
|
||||
* @param id Internal id of plugin. Saved globally by PLUGIN_SAVEVARS()
|
||||
* @param ismm External API for SourceMM. Saved globally by PLUGIN_SAVEVARS()
|
||||
* @param list Contains a list of factories. Hook a factory call by setting one equal to your own function.
|
||||
* @param id Internal id of plugin. Saved globally by PLUGIN_SAVEVARS()
|
||||
* @param ismm External API for SourceMM. Saved globally by PLUGIN_SAVEVARS()
|
||||
* @param list Contains a list of factories. Hook a factory call by setting one equal to your own function.
|
||||
* @param late Set to true if your plugin was loaded late (not at server load).
|
||||
* @param error Error message buffer
|
||||
* @param maxlen Size of error message buffer
|
||||
* @return True on success, return false to request no load.
|
||||
* @return True if successful, return false to reject the load.
|
||||
*/
|
||||
virtual bool Load(PluginId id, ISmmAPI *ismm, factories *list, char *error, size_t maxlen) =0;
|
||||
virtual bool Load(PluginId id, ISmmAPI *ismm, factories *list, char *error, size_t maxlength, bool late) =0;
|
||||
|
||||
/** @brief Called on plugin unload.
|
||||
/**
|
||||
* @brief Called when your plugin is "queried". This is useful for rejecting a loaded
|
||||
* state. For example, if your plugin wants to stop operating, it can simply return
|
||||
* false and copy an error message. This will notify other plugins or MM:S of something
|
||||
* bad that happened. NOTE - MM:S will not cache the return state, so if you return false,
|
||||
* your plugin will not actually be paused or unloaded. This callback will be called when:
|
||||
* - Another plugin requests it
|
||||
* - Someone types "meta list", it will show up as "REFUSED"
|
||||
* - When Metamod need to re-check the plugin's status
|
||||
* - If the plugin does something like overload a factory, Metamod will make sure the Query() returns true
|
||||
* before calling it.
|
||||
* Also note that this query will only override Metamod when the plugin is running and not paused.
|
||||
*
|
||||
* @param error Error message buffer
|
||||
* @param maxlen Size of error message buffer
|
||||
* @return True on success, return false to request no unload.
|
||||
* @param error Buffer for error message. This can be NULL!
|
||||
* @param maxlen Maximum length of error buffer.
|
||||
* @return Status code - true for okay, false for badness.
|
||||
*/
|
||||
virtual bool Unload(char *error, size_t maxlen) =0;
|
||||
virtual bool QueryRunning(char *error, size_t maxlen)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called on plugin unload. You can return false if you know your plugin
|
||||
* is not capable of restoring critical states it modifies.
|
||||
*
|
||||
* @param error Error message buffer
|
||||
* @param maxlen Size of error message buffer
|
||||
* @return True on success, return false to request no unload.
|
||||
*/
|
||||
virtual bool Unload(char *error, size_t maxlen)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @brief Called on plugin pause.
|
||||
*
|
||||
* @param error Error message buffer
|
||||
* @param maxlen Size of error message buffer
|
||||
* @return True on success, return false to request no pause.
|
||||
* @param error Error message buffer
|
||||
* @param maxlen Size of error message buffer
|
||||
* @return True on success, return false to request no pause.
|
||||
*/
|
||||
virtual bool Pause(char *error, size_t maxlen) =0;
|
||||
virtual bool Pause(char *error, size_t maxlen)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @brief Called on plugin unpause.
|
||||
*
|
||||
* @param error Error message buffer
|
||||
* @param maxlen Size of error message buffer
|
||||
* @return True on success, return false to request no unpause.
|
||||
* @param error Error message buffer
|
||||
* @param maxlen Size of error message buffer
|
||||
* @return True on success, return false to request no unpause.
|
||||
*/
|
||||
virtual bool Unpause(char *error, size_t maxlen) =0;
|
||||
virtual bool Unpause(char *error, size_t maxlen)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
public:
|
||||
/** @brief Return author as string */
|
||||
virtual const char *GetAuthor() =0;
|
||||
@ -101,13 +142,16 @@ public:
|
||||
public:
|
||||
/**
|
||||
* @brief Called when all plugins have been loaded - API version 4
|
||||
*
|
||||
* Ths is useful for knowing when it's safe to request things another plugin might have.
|
||||
*
|
||||
* This is useful for knowing when it's safe to request things another plugin might have.
|
||||
* NOTE for API 7 - This is called after DLLInit().
|
||||
*/
|
||||
virtual void AllPluginsLoaded() =0;
|
||||
virtual void AllPluginsLoaded()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
#define PL_EXPOSURE CreateInterface
|
||||
#define PL_EXPOSURE CreateInterface
|
||||
#define PL_EXPOSURE_C "CreateInterface"
|
||||
|
||||
#define PLUGIN_EXPOSE(name, var) \
|
||||
@ -138,13 +182,13 @@ public:
|
||||
g_SMAPI->SetLastMetaReturn(mres); \
|
||||
return value;
|
||||
|
||||
#define META_LOG g_SMAPI->LogMsg
|
||||
#define META_LOG g_SMAPI->LogMsg
|
||||
#define META_REGCMD(name) g_SMAPI->RegisterConCmdBase(g_PLAPI, name##_command)
|
||||
#define META_REGCVAR(var) g_SMAPI->RegisterConCmdBase(g_PLAPI, var)
|
||||
#define META_UNREGCMD(name) g_SMAPI->UnregisterConCmdBase(g_PLAPI, name##_command)
|
||||
#define META_UNREGCVAR(var) g_SMAPI->UnregisterConCmdBase(g_PLAPI, var)
|
||||
#define META_CONPRINT g_SMAPI->ConPrint
|
||||
#define META_CONPRINTF g_SMAPI->ConPrintf
|
||||
#define META_CONPRINT g_SMAPI->ConPrint
|
||||
#define META_CONPRINTF g_SMAPI->ConPrintf
|
||||
|
||||
//probably should use this up above someday
|
||||
#define CONCMD_VARNAME(name) name##_command
|
||||
|
@ -36,3 +36,25 @@ int GetLastError()
|
||||
return errno;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool GetFileOfAddress(void *pAddr, char *buffer, size_t maxlength)
|
||||
{
|
||||
#if defined WIN32 || defined _WIN32
|
||||
MEMORY_BASIC_INFORMATION mem;
|
||||
if (!VirtualQuery((void *)pAddr, &mem, sizeof(mem)))
|
||||
return false;
|
||||
if (mem.AllocationBase == NULL)
|
||||
return false;
|
||||
HMODULE dll = (HMODULE)mem.AllocationBase;
|
||||
GetModuleFileName(dll, (LPTSTR)buffer, maxlength);
|
||||
#elif defined __linux__
|
||||
Dl_info info;
|
||||
if (!dladdr((void *)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;
|
||||
}
|
||||
|
@ -27,6 +27,10 @@
|
||||
#define dlsym(x, s) GetProcAddress(x, s)
|
||||
#define dlclose(x) FreeLibrary(x)
|
||||
const char* dlerror();
|
||||
#define PATH_SEP_STR "\\"
|
||||
#define PATH_SEP_CHAR '\\'
|
||||
#define ALT_SEP_CHAR '/'
|
||||
#define SERVER_DLL "server.dll"
|
||||
#elif defined __linux__
|
||||
#define OS_LINUX
|
||||
#include <dlfcn.h>
|
||||
@ -35,6 +39,11 @@
|
||||
#include <dirent.h>
|
||||
#define dlmount(x) dlopen(x,RTLD_NOW)
|
||||
typedef void* HINSTANCE;
|
||||
#define PATH_SEP_STR "/"
|
||||
#define PATH_SEP_CHAR '/'
|
||||
#define ALT_SEP_CHAR '\\'
|
||||
#define stricmp strcasecmp
|
||||
#define SERVER_DLL "server_i486.so"
|
||||
#endif
|
||||
|
||||
#if defined __linux__
|
||||
@ -42,6 +51,8 @@
|
||||
int GetLastError();
|
||||
#endif
|
||||
|
||||
bool GetFileOfAddress(void *pAddr, char *buffer, size_t maxlength);
|
||||
|
||||
#if defined __WIN32__ || defined _WIN32 || defined WIN32
|
||||
#define SMM_API extern "C" __declspec(dllexport)
|
||||
#elif defined __GNUC__
|
||||
|
@ -12,10 +12,6 @@
|
||||
|
||||
#include <interface.h>
|
||||
#include <eiface.h>
|
||||
#include "CServerGameDLL.h"
|
||||
#include "CServerGameEnts.h"
|
||||
#include "CServerGameClients.h"
|
||||
#include "CHLTVDirector.h"
|
||||
#include "sourcemm.h"
|
||||
#include "concommands.h"
|
||||
#include "CSmmAPI.h"
|
||||
@ -26,262 +22,389 @@
|
||||
* @file sourcemm.cpp
|
||||
*/
|
||||
|
||||
CServerGameDLL g_TempGameDLL;
|
||||
CServerGameEnts g_TempGameEnts;
|
||||
CServerGameClients g_TempGameClients;
|
||||
CHLTVDirector g_TempDirector;
|
||||
GameDllInfo g_GameDll = {false, NULL, NULL};
|
||||
EngineInfo g_Engine = {NULL, NULL, NULL, NULL};
|
||||
SH_DECL_HOOK4(IServerGameDLL, DLLInit, SH_NOATTRIB, false, bool, CreateInterfaceFn, CreateInterfaceFn, CreateInterfaceFn, CGlobalVars *);
|
||||
SH_DECL_HOOK0_void(IServerGameDLL, DLLShutdown, SH_NOATTRIB, false);
|
||||
SH_DECL_HOOK0_void(IServerGameDLL, LevelShutdown, SH_NOATTRIB, false);
|
||||
SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, false, bool, const char *, const char *, const char *, const char *, bool, bool);
|
||||
bool DLLInit(CreateInterfaceFn engineFactory, CreateInterfaceFn physicsFactory, CreateInterfaceFn filesystemFactory, CGlobalVars *pGlobals);
|
||||
void DLLShutdown_handler();
|
||||
void LevelShutdown_handler();
|
||||
bool LevelInit_handler(char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background);
|
||||
|
||||
GameDllInfo g_GameDll = {false, NULL, NULL, NULL};
|
||||
EngineInfo g_Engine;
|
||||
SourceHook::CSourceHookImpl g_SourceHook;
|
||||
SourceHook::ISourceHook *g_SHPtr;
|
||||
SourceHook::ISourceHook *g_SHPtr = &g_SourceHook;
|
||||
SourceHook::String g_ModPath;
|
||||
SourceHook::String g_BinPath;
|
||||
PluginId g_PLID = Pl_Console; //Technically, SourceMM is the "Console" plugin... :p
|
||||
bool bInFirstLevel = true;
|
||||
bool gParsedGameInfo = false;
|
||||
SourceHook::List<GameDllInfo *> gamedll_list;
|
||||
SourceHook::CallClass<IServerGameDLL> *dllExec;
|
||||
|
||||
void ClearGamedllList();
|
||||
|
||||
///////////////////////////////////
|
||||
// Main code for HL2 Interaction //
|
||||
///////////////////////////////////
|
||||
|
||||
//Initialize everything here
|
||||
void InitMainStates()
|
||||
{
|
||||
char full_path[260] = {0};
|
||||
GetFileOfAddress(g_GameDll.factory, full_path, sizeof(full_path)-1);
|
||||
g_BinPath.assign(full_path);
|
||||
|
||||
UTIL_PathFmt(full_path, sizeof(full_path)-1, "%s/%s", g_ModPath.c_str(), GetPluginsFile());
|
||||
|
||||
//Like metamod, reload plugins at the end of the map.
|
||||
//This is so plugins can hook everything on load, BUT, new plugins will be reloaded
|
||||
// if the server is shut down (silly, but rare case).
|
||||
bInFirstLevel = true;
|
||||
|
||||
SH_ADD_HOOK_STATICFUNC(IServerGameDLL, DLLInit, g_GameDll.pGameDLL, DLLInit, false);
|
||||
SH_ADD_HOOK_STATICFUNC(IServerGameDLL, DLLShutdown, g_GameDll.pGameDLL, DLLShutdown_handler, false);
|
||||
SH_ADD_HOOK_STATICFUNC(IServerGameDLL, LevelShutdown, g_GameDll.pGameDLL, LevelShutdown_handler, true);
|
||||
SH_ADD_HOOK_STATICFUNC(IServerGameDLL, LevelInit, g_GameDll.pGameDLL, LevelInit_handler, true);
|
||||
}
|
||||
|
||||
bool DLLInit(CreateInterfaceFn engineFactory, CreateInterfaceFn physicsFactory, CreateInterfaceFn filesystemFactory, CGlobalVars *pGlobals)
|
||||
{
|
||||
g_Engine.engineFactory = engineFactory;
|
||||
g_Engine.fileSystemFactory = filesystemFactory;
|
||||
g_Engine.physicsFactory = physicsFactory;
|
||||
g_Engine.pGlobals = pGlobals;
|
||||
|
||||
g_Engine.engine = (IVEngineServer *)((engineFactory)(INTERFACEVERSION_VENGINESERVER, NULL));
|
||||
if (!g_Engine.engine)
|
||||
{
|
||||
Error("Could not find IVEngineServer! Metamod cannot load.");
|
||||
return false;
|
||||
}
|
||||
g_Engine.icvar = (ICvar *)((engineFactory)(VENGINE_CVAR_INTERFACE_VERSION , NULL));
|
||||
if (!g_Engine.icvar)
|
||||
{
|
||||
Error("Could not find ICvar! Metamod cannot load.");
|
||||
return false;
|
||||
}
|
||||
|
||||
g_Engine.loaded = true;
|
||||
|
||||
//Initialize our console hooks
|
||||
ConCommandBaseMgr::OneTimeInit(static_cast<IConCommandBaseAccessor *>(&g_SMConVarAccessor));
|
||||
|
||||
if (!g_SmmAPI.CacheCmds())
|
||||
{
|
||||
LogMessage("[META] Warning: Failed to initialize Con_Printf. Defaulting to Msg().");
|
||||
LogMessage("[META] Warning: Console messages will not be redirected to rcon console.");
|
||||
}
|
||||
|
||||
char full_path[260];
|
||||
UTIL_PathFmt(full_path, sizeof(full_path)-1, "%s/%s", g_ModPath.c_str(), GetPluginsFile());
|
||||
|
||||
LoadPluginsFromFile(full_path);
|
||||
|
||||
g_PluginMngr.SetAllLoaded();
|
||||
|
||||
bInFirstLevel = true;
|
||||
|
||||
dllExec = SH_GET_CALLCLASS(g_GameDll.pGameDLL);
|
||||
|
||||
RETURN_META_VALUE(MRES_IGNORED, true);
|
||||
}
|
||||
|
||||
//This is where the magic happens
|
||||
SMM_API void *CreateInterface(const char *name, int *ret)
|
||||
{
|
||||
if (!g_GameDll.loaded)
|
||||
if (!gParsedGameInfo)
|
||||
{
|
||||
//State of the Mod:
|
||||
// Currently, HL2 Engine has loaded Metamod:Source
|
||||
// It is now asking it to get an interface. We don't have one,
|
||||
// so we're gonna try to give it a fake one to get the information we need.
|
||||
// Then, the the interface will forward the calls to the original interface
|
||||
|
||||
if (strcmp(name, INTERFACEVERSION_SERVERGAMEDLL) == 0)
|
||||
gParsedGameInfo = true;
|
||||
char curpath[260] = {0};
|
||||
char dllpath[260] = {0};
|
||||
getcwd(curpath, sizeof(curpath)-1);
|
||||
if (!GetFileOfAddress((void *)CreateInterface, dllpath, sizeof(dllpath)-1))
|
||||
{
|
||||
//We're in. Give the server our fake class as bait.
|
||||
if (ret)
|
||||
*ret = IFACE_OK;
|
||||
return static_cast<void *>(&g_TempGameDLL);
|
||||
} else if (strcmp(name, INTERFACEVERSION_SERVERGAMEENTS) == 0) {
|
||||
if (ret)
|
||||
*ret = IFACE_OK;
|
||||
|
||||
return static_cast<void *>(&g_TempGameEnts);
|
||||
} else if (strcmp(name, INTERFACEVERSION_SERVERGAMECLIENTS) == 0) {
|
||||
if (ret)
|
||||
*ret = IFACE_OK;
|
||||
|
||||
return static_cast<void *>(&g_TempGameClients);
|
||||
} else if (strcmp(name, INTERFACEVERSION_HLTVDIRECTOR) == 0) {
|
||||
if (ret)
|
||||
*ret = IFACE_OK;
|
||||
|
||||
return static_cast<void *>(&g_TempDirector);
|
||||
} else {
|
||||
if (ret)
|
||||
*ret = IFACE_FAILED;
|
||||
|
||||
Error("GetFileOfAddress() failed! Metamod cannot load.");
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
META_INTERFACE_MACRO(server, g_GameDll.factory);
|
||||
}
|
||||
}
|
||||
SourceHook::String s_dllpath(dllpath);
|
||||
//begin the heuristics for searching for the mod path (these are quite ugly).
|
||||
//for OS compatibility purposes, we're going to do case insensitivity on windows.
|
||||
size_t path_len = strlen(curpath);
|
||||
size_t dll_len = strlen(dllpath);
|
||||
|
||||
bool CServerGameDLL::DLLInit(CreateInterfaceFn engineFactory, CreateInterfaceFn physicsFactory, CreateInterfaceFn fileSystemFactory, CGlobalVars *pGlobals)
|
||||
{
|
||||
if (m_pOrig)
|
||||
return m_pOrig->DLLInit(engineFactory, physicsFactory, fileSystemFactory, pGlobals);
|
||||
//strip the dll path off
|
||||
//:TODO: with path stuff - in Linux, \ can exist in a file path as a non-seperator!
|
||||
for (size_t i=dll_len-1; i>0; i--)
|
||||
{
|
||||
if (dllpath[i] == '\\' || dllpath[i] == '/')
|
||||
{
|
||||
if (i == dll_len-1)
|
||||
break;
|
||||
//save path by stripping off file name and ending terminator
|
||||
dllpath[i] = '\0';
|
||||
dll_len = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//strip absolute path terminators if any!
|
||||
if (curpath[path_len-1] == '/' || curpath[path_len-1] == '\\')
|
||||
curpath[--path_len] = '\0';
|
||||
|
||||
//if the base path doesn't fit into the module path, something is wrong!
|
||||
// ex: c:\games\srcds
|
||||
// c:\gaben.dll
|
||||
if (path_len > dll_len)
|
||||
{
|
||||
Error("Could not detect GameDLL path! Metamod cannot load[1].");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//we are now in such a position that the two dir names SHOULD MATCH!
|
||||
typedef int (*STRNCMP)(const char *str1, const char *str2, size_t n);
|
||||
STRNCMP cmp =
|
||||
#ifdef WIN32
|
||||
strnicmp;
|
||||
#else
|
||||
strncmp;
|
||||
#endif
|
||||
//are they equal?
|
||||
if ( ((cmp)(curpath, dllpath, path_len)) != 0 )
|
||||
{
|
||||
//:TODO: In this case, we should read /proc/self/maps and find srcds!
|
||||
Error("Could not detect GameDLL path! Metamod cannot load[2].");
|
||||
return NULL;
|
||||
}
|
||||
//this will skip past the dir and its separator char
|
||||
char *ptr = &(dllpath[path_len+1]);
|
||||
path_len = strlen(ptr);
|
||||
for (size_t i=0; i<path_len; i++)
|
||||
{
|
||||
if (ptr[i] == '/' || ptr[i] == '\\')
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
Error("Could not detect GameDLL path! Metamod cannot load[3].");
|
||||
return NULL;
|
||||
} else {
|
||||
ptr[i] = '\0';
|
||||
path_len = i+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//WE NOW HAVE A GUESS AT THE MOD DIR. OH MY GOD.
|
||||
char temp_path[260];
|
||||
snprintf(temp_path, sizeof(temp_path)-1,
|
||||
"%s%s%s",
|
||||
curpath,
|
||||
PATH_SEP_STR,
|
||||
ptr);
|
||||
|
||||
g_ModPath.assign(temp_path);
|
||||
|
||||
snprintf(temp_path, sizeof(temp_path)-1,
|
||||
"%s%s%s",
|
||||
g_ModPath.c_str(),
|
||||
PATH_SEP_STR,
|
||||
"gameinfo.txt");
|
||||
|
||||
FILE *fp = fopen(temp_path, "rt");
|
||||
if (!fp)
|
||||
{
|
||||
Error("Unable to open gameinfo.txt! Metamod cannot load.");
|
||||
return NULL;
|
||||
}
|
||||
char buffer[255];
|
||||
char key[128], val[128];
|
||||
size_t len = 0;
|
||||
const char *gameinfo = "|gameinfo_path|";
|
||||
size_t gameinfo_len = strlen(gameinfo);
|
||||
bool search = false;
|
||||
bool gamebin = false;
|
||||
const char *lptr;
|
||||
while (!feof(fp))
|
||||
{
|
||||
buffer[0] = '\0';
|
||||
fgets(buffer, sizeof(buffer)-1, fp);
|
||||
len = strlen(buffer);
|
||||
if (buffer[len-1] == '\n')
|
||||
buffer[--len] = '\0';
|
||||
UTIL_TrimComments(buffer);
|
||||
UTIL_TrimLeft(buffer);
|
||||
UTIL_TrimRight(buffer);
|
||||
if (stricmp(buffer, "SearchPaths") == 0)
|
||||
search = true;
|
||||
if (!search)
|
||||
continue;
|
||||
UTIL_KeySplit(buffer, key, sizeof(key)-1, val, sizeof(val)-1);
|
||||
if (stricmp(key, "Game") == 0 || stricmp(key, "GameBin") == 0)
|
||||
{
|
||||
if (stricmp(key, "Game") == 0)
|
||||
gamebin = false;
|
||||
else
|
||||
gamebin = true;
|
||||
|
||||
if (strncmp(val, gameinfo, gameinfo_len) == 0)
|
||||
{
|
||||
ptr = &(val[gameinfo_len]);
|
||||
if (ptr[0] == '.')
|
||||
ptr++;
|
||||
lptr = g_ModPath.c_str();
|
||||
} else {
|
||||
lptr = curpath;
|
||||
ptr = val;
|
||||
}
|
||||
size_t ptr_len = strlen(ptr);
|
||||
if (ptr[ptr_len] == '/' || ptr[ptr_len] == '\\')
|
||||
ptr[--ptr_len] = '\0';
|
||||
//no need to append "bin"
|
||||
if (gamebin)
|
||||
{
|
||||
UTIL_PathFmt(temp_path, sizeof(temp_path)-1, "%s/%s/%s", lptr, ptr, SERVER_DLL);
|
||||
} else {
|
||||
UTIL_PathFmt(temp_path, sizeof(temp_path)-1, "%s/%s/%s/%s", lptr, ptr, "bin", SERVER_DLL);
|
||||
}
|
||||
if (!UTIL_PathCmp(s_dllpath.c_str(), temp_path))
|
||||
{
|
||||
FILE *fp = fopen(temp_path, "rb");
|
||||
if (!fp)
|
||||
continue;
|
||||
//:TODO: Optimize this a bit!
|
||||
SourceHook::List<GameDllInfo *>::iterator iter;
|
||||
GameDllInfo *pCheck;
|
||||
bool found = false;
|
||||
for (iter=gamedll_list.begin(); iter!=gamedll_list.end(); iter++)
|
||||
{
|
||||
pCheck = (*iter);
|
||||
if (GetFileOfAddress(pCheck->factory, buffer, sizeof(buffer)-1))
|
||||
{
|
||||
if (UTIL_PathCmp(temp_path, buffer))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
continue;
|
||||
fclose(fp);
|
||||
HINSTANCE gamedll = dlmount(temp_path);
|
||||
if (gamedll == NULL)
|
||||
continue;
|
||||
CreateInterfaceFn fn = (CreateInterfaceFn)dlsym(gamedll, "CreateInterface");
|
||||
if (fn == NULL)
|
||||
{
|
||||
dlclose(gamedll);
|
||||
continue;
|
||||
}
|
||||
GameDllInfo *pInfo = new GameDllInfo;
|
||||
pInfo->factory = fn;
|
||||
pInfo->lib = gamedll;
|
||||
pInfo->loaded = true;
|
||||
pInfo->pGameDLL = NULL;
|
||||
gamedll_list.push_back(pInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
if (!g_GameDll.loaded)
|
||||
{
|
||||
//Initialize SourceHook
|
||||
g_SHPtr = static_cast<SourceHook::ISourceHook *>(&g_SourceHook);
|
||||
const char *str = "ServerGameDLL";
|
||||
size_t len = strlen(str);
|
||||
|
||||
//The gamedll isn't loaded yet. We need to find out where it's hiding.
|
||||
IVEngineServer *ive = (IVEngineServer *)((engineFactory)(INTERFACEVERSION_VENGINESERVER, NULL));
|
||||
if (!ive)
|
||||
if (strncmp(name, str, len) == 0)
|
||||
{
|
||||
Error("Metamod:Source could not load %s", INTERFACEVERSION_VENGINESERVER);
|
||||
return false;
|
||||
}
|
||||
|
||||
//Guess the file name
|
||||
//We'll have better heurestics[sp?] later on
|
||||
char mod_path[128], full_path[255];
|
||||
ive->GetGameDir(mod_path, sizeof(mod_path)-1);
|
||||
g_ModPath.assign(mod_path);
|
||||
#if defined WIN32 || defined _WIN32
|
||||
snprintf(full_path, sizeof(full_path)-1, "%s\\bin\\server.dll", mod_path);
|
||||
#else
|
||||
snprintf(full_path, sizeof(full_path)-1, "%s/bin/server_i486.so", mod_path);
|
||||
#endif
|
||||
|
||||
g_BinPath.assign(full_path);
|
||||
|
||||
//See if the file even exists
|
||||
FILE *fp = fopen(g_BinPath.c_str(), "r");
|
||||
if (!fp)
|
||||
{
|
||||
Error("Metamod:Source could not read %s", g_BinPath.c_str());
|
||||
return false;
|
||||
}
|
||||
fclose(fp);
|
||||
fp = NULL;
|
||||
|
||||
//Load the DLL
|
||||
g_GameDll.lib = dlmount(g_BinPath.c_str());
|
||||
if (!g_GameDll.lib)
|
||||
{
|
||||
Error("Metamod:Source could not load GameDLL: %s", dlerror());
|
||||
return false;
|
||||
//This is the interface we want! Right now we support versions 3 and 4.
|
||||
int version = atoi(&(name[len]));
|
||||
if (version < MIN_GAMEDLL_VERSION || version > MAX_GAMEDLL_VERSION)
|
||||
{
|
||||
Error("GameDLL version %d is not supported by Metamod!", version);
|
||||
return NULL;
|
||||
}
|
||||
SourceHook::List<GameDllInfo *>::iterator iter;
|
||||
GameDllInfo *pInfo = NULL;
|
||||
void *ptr;
|
||||
for (iter=gamedll_list.begin(); iter!=gamedll_list.end(); iter++)
|
||||
{
|
||||
pInfo = (*iter);
|
||||
ptr = (pInfo->factory)(name, ret);
|
||||
if (ptr)
|
||||
{
|
||||
//this is our gamedll. unload the others.
|
||||
gamedll_list.erase(iter);
|
||||
ClearGamedllList();
|
||||
pInfo->pGameDLL = static_cast<IServerGameDLL *>(ptr);
|
||||
g_GameDll = *pInfo;
|
||||
delete pInfo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (g_GameDll.loaded)
|
||||
{
|
||||
InitMainStates();
|
||||
} else {
|
||||
if (ret)
|
||||
*ret = IFACE_FAILED;
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
//Find its factory
|
||||
g_GameDll.factory = (CreateInterfaceFn)(dlsym(g_GameDll.lib, "CreateInterface"));
|
||||
if (!g_GameDll.factory)
|
||||
{
|
||||
Error("Metamod:Source could not find an entry point in GameDLL: %s", g_BinPath.c_str());
|
||||
dlclose(g_GameDll.lib);
|
||||
return false;
|
||||
}
|
||||
|
||||
//Find the new IServerGameDLL pointer
|
||||
IServerGameDLL *serverDll;
|
||||
serverDll = (IServerGameDLL *)((g_GameDll.factory)(INTERFACEVERSION_SERVERGAMEDLL, NULL));
|
||||
if (!serverDll)
|
||||
{
|
||||
Error("Metamod:Source could not find %s in GameDLL: %s", INTERFACEVERSION_SERVERGAMEDLL, g_BinPath.c_str());
|
||||
dlclose(g_GameDll.lib);
|
||||
return false;
|
||||
}
|
||||
|
||||
//Set this information early in case our wrappers are called somehow
|
||||
g_Engine.engineFactory = engineFactory;
|
||||
g_Engine.icvar = (ICvar *)(g_Engine.engineFactory)(VENGINE_CVAR_INTERFACE_VERSION, NULL);
|
||||
if (!g_Engine.icvar)
|
||||
{
|
||||
Error("Metamod:Source could not find %s in engine!", VENGINE_CVAR_INTERFACE_VERSION);
|
||||
dlclose(g_GameDll.lib);
|
||||
return false;
|
||||
}
|
||||
g_Engine.fileSystemFactory = fileSystemFactory;
|
||||
g_Engine.pGlobals = pGlobals;
|
||||
g_Engine.physicsFactory = physicsFactory;
|
||||
g_Engine.engine = ive;
|
||||
|
||||
//Attempt to load the GameDLL
|
||||
// Note that nothing will be intercepting yet.
|
||||
// This is the one and only call that plugins have no chance of seeing.
|
||||
// Likewise, you won't be able to trick the Server DLL into loading random things.
|
||||
// Luckily, because of SourceHook, this really isn't a problem - you can change
|
||||
// the virtual interfaces in anything it requests.
|
||||
if (!serverDll->DLLInit(EngineFactory, PhysicsFactory, FileSystemFactory, pGlobals))
|
||||
{
|
||||
//For some reason, the GameDLL failed to load.
|
||||
Error("Metamod:Source: GameDLL %s refused to load.", g_BinPath.c_str());
|
||||
dlclose(g_GameDll.lib);
|
||||
return false;
|
||||
}
|
||||
|
||||
//Retrieve the pointers we'll need from the GameDLL
|
||||
IServerGameEnts *serverEnts;
|
||||
IServerGameClients *serverClients;
|
||||
IHLTVDirector *serverHLTV;
|
||||
|
||||
serverEnts = (IServerGameEnts *)((g_GameDll.factory)(INTERFACEVERSION_SERVERGAMEENTS, NULL));
|
||||
if (!serverEnts)
|
||||
{
|
||||
Error("Metamod:Source could not find %s in GameDLL: %s", INTERFACEVERSION_SERVERGAMEENTS, g_BinPath.c_str());
|
||||
dlclose(g_GameDll.lib);
|
||||
return false;
|
||||
}
|
||||
|
||||
serverClients = (IServerGameClients *)((g_GameDll.factory)(INTERFACEVERSION_SERVERGAMECLIENTS, NULL));
|
||||
if (!serverClients)
|
||||
{
|
||||
Error("Metamod:Source could not find %s in GameDLL: %s", INTERFACEVERSION_SERVERGAMECLIENTS, g_BinPath.c_str());
|
||||
dlclose(g_GameDll.lib);
|
||||
return false;
|
||||
}
|
||||
|
||||
serverHLTV = (IHLTVDirector *)((g_GameDll.factory)(INTERFACEVERSION_HLTVDIRECTOR, NULL));
|
||||
if (!serverHLTV)
|
||||
{
|
||||
Error("Metamod:Source could not find %s in GameDLL: %s", INTERFACEVERSION_HLTVDIRECTOR, g_BinPath.c_str());
|
||||
dlclose(g_GameDll.lib);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now tell the global temp classes that they can call the original functions
|
||||
g_TempDirector.SetOrig(serverHLTV);
|
||||
g_TempGameClients.SetOrig(serverClients);
|
||||
g_TempGameEnts.SetOrig(serverEnts);
|
||||
g_TempGameDLL.SetOrig(serverDll);
|
||||
|
||||
//Everything's done.
|
||||
g_GameDll.loaded = true;
|
||||
|
||||
//Initialize our console hooks
|
||||
ConCommandBaseMgr::OneTimeInit(static_cast<IConCommandBaseAccessor *>(&g_SMConVarAccessor));
|
||||
|
||||
if (!g_SmmAPI.CacheCmds())
|
||||
{
|
||||
LogMessage("[META] Warning: Failed to initialize Con_Printf. Defaulting to Msg().");
|
||||
LogMessage("[META] Warning: Console messages will not be redirected to rcon console.");
|
||||
}
|
||||
|
||||
//Now it's safe to load plugins.
|
||||
#if defined WIN32 || defined _WIN32
|
||||
snprintf(full_path, sizeof(full_path)-1, "%s\\%s", g_ModPath.c_str(), GetPluginsFile());
|
||||
#else
|
||||
snprintf(full_path, sizeof(full_path)-1, "%s/%s", g_ModPath.c_str(), GetPluginsFile());
|
||||
#endif
|
||||
|
||||
LoadPluginsFromFile(full_path);
|
||||
|
||||
//All plugins are now loaded.
|
||||
g_PluginMngr.SetAllLoaded();
|
||||
|
||||
//Like metamod, reload plugins at the end of the map.
|
||||
//This is so plugins can hook everything on load, BUT, new plugins will be reloaded
|
||||
// if the server is shut down (silly, but rare case).
|
||||
bInFirstLevel = true;
|
||||
|
||||
return true;
|
||||
//wtf do we do...
|
||||
//:TODO: .. something a bit more intelligent?
|
||||
Error("Engine requested unknown interface before GameDLL was known!");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//Somehow, the function got here. This should be impossible.
|
||||
Error("Metamod:Source fatal error - IServerGameDLL::DLLInit() called inappropriately");
|
||||
SetUnhandledExceptionFilter(NULL);
|
||||
|
||||
return false;
|
||||
//if we got here, there's definitely a gamedll.
|
||||
//META_INTERFACE_MACRO(server, g_GameDll.factory);
|
||||
return (g_GameDll.factory)(name, ret);
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
void ClearGamedllList()
|
||||
{
|
||||
SourceHook::List<GameDllInfo *>::iterator iter;
|
||||
|
||||
GameDllInfo *pInfo;
|
||||
for (iter=gamedll_list.begin(); iter!=gamedll_list.end(); iter++)
|
||||
{
|
||||
pInfo = (*iter);
|
||||
dlclose(pInfo->lib);
|
||||
delete pInfo;
|
||||
}
|
||||
|
||||
gamedll_list.clear();
|
||||
}
|
||||
|
||||
void DLLShutdown_handler()
|
||||
{
|
||||
//Unload plugins
|
||||
g_PluginMngr.UnloadAll();
|
||||
|
||||
// Shutdown sourcehook now
|
||||
g_SourceHook.CompleteShutdown();
|
||||
|
||||
// Add the FCVAR_GAMEDLL flag to our cvars so the engine removes them properly
|
||||
g_SMConVarAccessor.MarkCommandsAsGameDLL();
|
||||
}
|
||||
|
||||
void CServerGameDLL::DLLShutdown()
|
||||
{
|
||||
Shutdown();
|
||||
|
||||
//Call the original function
|
||||
m_pOrig->DLLShutdown();
|
||||
|
||||
//Unregister all commands marked as GameDLL now
|
||||
//This prevents crashes when the engine tries to unregister them once we have already unloaded the gamedll
|
||||
//(we used to unload the gamedll in a __attribute__((destructor)) function but I've had problems with that (crashes in dlclose)
|
||||
g_SMConVarAccessor.UnregisterGameDLLCommands();
|
||||
|
||||
SH_CALL(dllExec, &IServerGameDLL::DLLShutdown)();
|
||||
|
||||
SH_RELEASE_CALLCLASS(dllExec);
|
||||
dllExec = NULL;
|
||||
|
||||
//right now this will crash when the function returns!
|
||||
// :TODO: remove this warning once PM fixes it.
|
||||
g_SourceHook.CompleteShutdown();
|
||||
|
||||
if (g_GameDll.lib && g_GameDll.loaded)
|
||||
dlclose(g_GameDll.lib);
|
||||
memset(&g_GameDll, 0, sizeof(GameDllInfo));
|
||||
|
||||
RETURN_META(MRES_SUPERCEDE);
|
||||
}
|
||||
|
||||
int LoadPluginsFromFile(const char *file)
|
||||
@ -345,11 +468,7 @@ int LoadPluginsFromFile(const char *file)
|
||||
ext = "";
|
||||
}
|
||||
//Format the new path
|
||||
#if defined WIN32 || defined _WIN32
|
||||
snprintf(full_path, sizeof(full_path)-1, "%s\\%s%s", g_ModPath.c_str(), buffer, ext);
|
||||
#else
|
||||
snprintf(full_path, sizeof(full_path)-1, "%s/%s%s", g_ModPath.c_str(), buffer, ext);
|
||||
#endif
|
||||
UTIL_PathFmt(full_path, sizeof(full_path)-1, "%s/%s", g_ModPath.c_str(), buffer, ext);
|
||||
id = g_PluginMngr.Load(full_path, Pl_File, already, error, sizeof(error)-1);
|
||||
if (id < Pl_MinId || g_PluginMngr.FindById(id)->m_Status < Pl_Paused)
|
||||
{
|
||||
@ -407,13 +526,12 @@ void LogMessage(const char *msg, ...)
|
||||
strcat(buffer, "\n");
|
||||
va_end(ap);
|
||||
|
||||
g_Engine.engine->LogPrint(buffer);
|
||||
}
|
||||
|
||||
void CServerGameDLL::LevelShutdown(void)
|
||||
{
|
||||
LevelShutdown_handler();
|
||||
m_pOrig->LevelShutdown();
|
||||
if (!g_Engine.engine)
|
||||
{
|
||||
fprintf(stdout, "%s", buffer);
|
||||
} else {
|
||||
g_Engine.engine->LogPrint(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void LevelShutdown_handler(void)
|
||||
@ -432,7 +550,7 @@ void LevelShutdown_handler(void)
|
||||
}
|
||||
}
|
||||
|
||||
bool CServerGameDLL::LevelInit( char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background )
|
||||
bool LevelInit_handler(char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background)
|
||||
{
|
||||
if (!g_SmmAPI.CacheSuccessful())
|
||||
{
|
||||
@ -440,19 +558,7 @@ bool CServerGameDLL::LevelInit( char const *pMapName, char const *pMapEntities,
|
||||
LogMessage("[META] Warning: Console messages will not be redirected to rcon console.");
|
||||
}
|
||||
|
||||
return m_pOrig->LevelInit(pMapName, pMapEntities, pOldLevel, pLandmarkName, loadGame, background);
|
||||
}
|
||||
|
||||
const char *CServerGameDLL::GetGameDescription()
|
||||
{
|
||||
if (m_pOrig)
|
||||
{
|
||||
const char *game = m_pOrig->GetGameDescription();
|
||||
if (game)
|
||||
strcpy(m_GameDescBuffer, game);
|
||||
}
|
||||
|
||||
return m_GameDescBuffer;
|
||||
RETURN_META_VALUE(MRES_IGNORED, false);
|
||||
}
|
||||
|
||||
#if defined __GNUC__ && (__GNUC__ == 3)
|
||||
|
@ -29,6 +29,9 @@
|
||||
#define SOURCEMM_VERSION "1.10"
|
||||
#define SOURCEMM_DATE __DATE__
|
||||
|
||||
#define MIN_GAMEDLL_VERSION 3
|
||||
#define MAX_GAMEDLL_VERSION 4
|
||||
|
||||
/**
|
||||
* @brief Entry point for HL2 Engine
|
||||
*/
|
||||
@ -55,11 +58,17 @@ struct GameDllInfo
|
||||
bool loaded;
|
||||
HINSTANCE lib;
|
||||
CreateInterfaceFn factory;
|
||||
IServerGameDLL *pGameDLL;
|
||||
};
|
||||
|
||||
/** @brief Stores information about the HL2 Engine pointers */
|
||||
struct EngineInfo
|
||||
{
|
||||
EngineInfo() : loaded(false),
|
||||
engineFactory(NULL), physicsFactory(NULL), fileSystemFactory(NULL),
|
||||
pGlobals(NULL), icvar(NULL), engine(NULL)
|
||||
{ };
|
||||
bool loaded;
|
||||
CreateInterfaceFn engineFactory;
|
||||
CreateInterfaceFn physicsFactory;
|
||||
CreateInterfaceFn fileSystemFactory;
|
||||
@ -89,7 +98,4 @@ extern SourceHook::ISourceHook *g_SHPtr;
|
||||
/** @brief We have our own internal plugin id... */
|
||||
extern PluginId g_PLID;
|
||||
|
||||
/** @brief Hook for LevelShutdown */
|
||||
void LevelShutdown_handler(void);
|
||||
|
||||
#endif //_INCLUDE_SOURCEMM_H
|
||||
|
@ -154,33 +154,18 @@
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
|
||||
<File
|
||||
RelativePath=".\CHLTVDirector.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\concommands.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\CPlugin.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\CServerGameClients.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\CServerGameDLL.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\CServerGameEnts.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\CSmmAPI.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\oslink.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\resource.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\sourcemm.h">
|
||||
</File>
|
||||
|
@ -10,7 +10,10 @@
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include "util.h"
|
||||
#include "oslink.h"
|
||||
|
||||
/**
|
||||
* @brief Utility functons
|
||||
@ -55,11 +58,43 @@ void UTIL_TrimLeft(char *buffer)
|
||||
i++;
|
||||
|
||||
// If whitespace chars in buffer then adjust string so first non-whitespace char is at start of buffer
|
||||
// :TODO: change this to not use memcpy()!
|
||||
if (i != buffer)
|
||||
memcpy(buffer, i, (strlen(i) + 1) * sizeof(char));
|
||||
}
|
||||
}
|
||||
|
||||
//:TODO: this should skip string literals
|
||||
void UTIL_TrimComments(char *buffer)
|
||||
{
|
||||
int num_sc = 0;
|
||||
size_t len = strlen(buffer);
|
||||
if (buffer)
|
||||
{
|
||||
for (int i=(int)len-1; i>=0; i--)
|
||||
{
|
||||
if (buffer[i] == '/')
|
||||
{
|
||||
if (++num_sc >= 2 && i==0)
|
||||
{
|
||||
buffer[i] = '\0';
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (num_sc >= 2)
|
||||
{
|
||||
buffer[i] = '\0';
|
||||
return;
|
||||
}
|
||||
num_sc = 0;
|
||||
}
|
||||
//size_t won't go below 0, manually break out
|
||||
if (i == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* UTIL_TrimLeft
|
||||
* Removes whitespace characters from right side of string
|
||||
*/
|
||||
@ -69,7 +104,7 @@ void UTIL_TrimRight(char *buffer)
|
||||
if (buffer)
|
||||
{
|
||||
// Loop through buffer backwards while replacing whitespace chars with null chars
|
||||
for (unsigned int i = strlen(buffer) - 1; i >= 0; i--)
|
||||
for (int i = (int)strlen(buffer) - 1; i >= 0; i--)
|
||||
{
|
||||
if (isspace(buffer[i]))
|
||||
buffer[i] = '\0';
|
||||
@ -79,3 +114,110 @@ void UTIL_TrimRight(char *buffer)
|
||||
}
|
||||
}
|
||||
|
||||
/* UTIL_KeySplit
|
||||
* Breaks a string at the first space until it reaches a nonspace
|
||||
*/
|
||||
void UTIL_KeySplit(const char *str, char *buf1, size_t len1, char *buf2, size_t len2)
|
||||
{
|
||||
size_t start;
|
||||
size_t len = strlen(str);
|
||||
|
||||
for (start=0; start<len; start++)
|
||||
{
|
||||
if (!isspace(str[start]))
|
||||
break;
|
||||
}
|
||||
|
||||
size_t end;
|
||||
for (end=start; end<len; end++)
|
||||
{
|
||||
if (isspace(str[end]))
|
||||
break;
|
||||
}
|
||||
|
||||
size_t i, c=0;
|
||||
for (i=start; i<end; i++,c++)
|
||||
{
|
||||
if (c >= len1)
|
||||
break;
|
||||
buf1[c] = str[i];
|
||||
}
|
||||
buf1[c] = '\0';
|
||||
|
||||
for (start=end; start<len; start++)
|
||||
{
|
||||
if (!isspace(str[start]))
|
||||
break;
|
||||
}
|
||||
|
||||
for (c=0; start<len; start++,c++)
|
||||
{
|
||||
if (c >= len2)
|
||||
break;
|
||||
buf2[c] = str[start];
|
||||
}
|
||||
buf2[c] = '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a path name for an OS
|
||||
*/
|
||||
void UTIL_PathFmt(char *buffer, size_t len, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap,fmt);
|
||||
size_t mylen = vsnprintf(buffer, len, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
for (size_t i=0; i<mylen; i++)
|
||||
{
|
||||
if (buffer[i] == ALT_SEP_CHAR)
|
||||
buffer[i] = PATH_SEP_CHAR;
|
||||
}
|
||||
}
|
||||
|
||||
bool UTIL_PathCmp(const char *path1, const char *path2)
|
||||
{
|
||||
size_t len1=strlen(path1);
|
||||
size_t len2=strlen(path2);
|
||||
size_t pos1=0,pos2=0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (path1[pos1] == '\0' || path2[pos2] == '\0')
|
||||
{
|
||||
return (path1[pos1] == path2[pos2]);
|
||||
}
|
||||
|
||||
if (path1[pos1] == PATH_SEP_CHAR)
|
||||
{
|
||||
if (path2[pos2] != PATH_SEP_CHAR)
|
||||
return false;
|
||||
//look for extra path chars
|
||||
while (path1[++pos1])
|
||||
{
|
||||
if (path1[pos1] != PATH_SEP_CHAR)
|
||||
break;
|
||||
}
|
||||
while (path2[++pos2])
|
||||
{
|
||||
if (path2[pos2] != PATH_SEP_CHAR)
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
//if we're at a different non-alphanumeric, the next character MUST Match
|
||||
if (!isalpha(path1[pos1]) && (path1[pos1] != path2[pos2]))
|
||||
return false;
|
||||
|
||||
#ifdef WIN32
|
||||
if (toupper(path1[pos1]) != toupper(path2[pos2]))
|
||||
#else
|
||||
if (path1[pos1] != path2[pos2])
|
||||
#endif
|
||||
return false;
|
||||
pos1++;
|
||||
pos2++;
|
||||
}
|
||||
}
|
||||
|
@ -17,8 +17,12 @@
|
||||
*/
|
||||
|
||||
const char *UTIL_GetExtension(const char *file);
|
||||
void UTIL_TrimComments(char *buffer);
|
||||
void UTIL_TrimLeft(char *buffer);
|
||||
void UTIL_TrimRight(char *buffer);
|
||||
void UTIL_KeySplit(const char *str, char *buf1, size_t len1, char *buf2, size_t len2);
|
||||
void UTIL_PathFmt(char *buffer, size_t len, const char *fmt, ...);
|
||||
bool UTIL_PathCmp(const char *path1, const char *path2);
|
||||
|
||||
#define META_INTERFACE_MACRO(type, final) \
|
||||
PluginIter i; \
|
||||
@ -39,12 +43,15 @@ void UTIL_TrimRight(char *buffer);
|
||||
} \
|
||||
if (high == MRES_OVERRIDE) \
|
||||
{ \
|
||||
(final)(name, ret); \
|
||||
if (final) \
|
||||
(final)(name, ret); \
|
||||
return mret; \
|
||||
} else if (high == MRES_SUPERCEDE) { \
|
||||
return mret; \
|
||||
} else { \
|
||||
return (final)(name, ret); \
|
||||
if (final) \
|
||||
return (final)(name, ret); \
|
||||
return NULL; \
|
||||
}
|
||||
|
||||
#endif //_INCLUDE_UTIL_H
|
||||
|
Loading…
x
Reference in New Issue
Block a user