1
0
mirror of https://github.com/alliedmodders/metamod-source.git synced 2025-03-21 12:28:56 +01:00

Added experimental user message API functions that don't error out on invalid indices

Added user message list to 'meta game' command

Terrible, ugly code...

--HG--
extra : convert_revision : svn%3Ac2935e3e-5518-0410-8daf-afa5dab7d4e3/trunk%40338
This commit is contained in:
Scott Ehlert 2007-02-21 01:51:54 +00:00
parent c7a31de575
commit bf3d98f77c
8 changed files with 223 additions and 17 deletions

View File

@ -27,10 +27,16 @@ CSmmAPI g_SmmAPI;
CSmmAPI::CSmmAPI()
{
m_ConPrintf = NULL;
m_Cache = false;
m_CmdCache = false;
m_MsgCount = -1;
m_VSP = false;
}
CSmmAPI::~CSmmAPI()
{
m_UserMessages.RemoveAll();
}
void CSmmAPI::LogMsg(ISmmPlugin *pl, const char *msg, ...)
{
va_list ap;
@ -279,7 +285,7 @@ bool CSmmAPI::CacheCmds()
//add the base offset, to the ip (which is the address+offset + 4 bytes for next instruction)
m_ConPrintf = (CONPRINTF_FUNC)((unsigned long)m_ConPrintf + (unsigned long)(ptr + offs) + 4);
m_Cache = true;
m_CmdCache = true;
return true;
}
@ -291,9 +297,9 @@ bool CSmmAPI::CacheCmds()
return false;
}
bool CSmmAPI::CacheSuccessful()
bool CSmmAPI::CmdCacheSuccessful()
{
return m_Cache;
return m_CmdCache;
}
void CSmmAPI::GetApiVersions(int &major, int &minor, int &plvers, int &plmin)
@ -498,3 +504,104 @@ int CSmmAPI::GetGameDLLVersion()
{
return g_GameDllVersion;
}
//////////////////////////////////////////////////////////////////////
// EVEN MORE HACKS HERE! YOU HAVE BEEN WARNED! //
// Signatures necessary in finding the pointer to the CUtlDict that //
// stores user message information. //
// IServerGameDLL::GetUserMessageInfo() normally crashes with bad //
// message indices. This is our answer to it. Yuck! <:-( //
//////////////////////////////////////////////////////////////////////
#ifdef OS_WIN32
#define MSGCLASS_SIGLEN 7
#define MSGCLASS_SIG "\x8B\x0D\x2A\x2A\x2A\x2A\x56"
#define MSGCLASS_OFFS 2
#elif defined OS_LINUX
#define MSGCLASS_SIGLEN 14
#define MSGCLASS_SIG "\x53\x83\xEC\x2A\x8B\x5C\x2A\x2A\xA1\x2A\x2A\x2A\x2A\x89"
#define MSGCLASS_OFFS 9
#endif
/* This is the ugliest function in all of SourceMM */
bool CSmmAPI::CacheUserMessages()
{
SourceHook::MemFuncInfo info = {true, -1, 0, 0};
SourceHook::GetFuncInfo(&IServerGameDLL::GetUserMessageInfo, info);
// Get address of original GetUserMessageInfo()
char *vfunc = reinterpret_cast<char *>(g_GameDllPatch->GetOrigFunc(info.vtbloffs, info.vtblindex));
// If we can't get original function (GetOrigFunc bug?)
if (vfunc == NULL)
{
// This means there's no hook, so we must get it manually - Lovely code <:-(
char *adjustedptr = reinterpret_cast<char *>(g_GameDll.pGameDLL) + info.thisptroffs + info.vtbloffs;
char **vtable = *reinterpret_cast<char ***>(adjustedptr);
vfunc = vtable[info.vtblindex];
}
if (vcmp(vfunc, MSGCLASS_SIG, MSGCLASS_SIGLEN))
{
// Get address of CUserMessages
char **userMsgClass = *reinterpret_cast<char ***>(vfunc + MSGCLASS_OFFS);
// Get address of CUserMessages::m_UserMessages
UserMsgDict *dict = reinterpret_cast<UserMsgDict *>(*userMsgClass);
m_MsgCount = dict->Count();
// Cache messages in our CUtlDict
UserMessage *msg;
for (int i = 0; i < m_MsgCount; i++)
{
msg = dict->Element(i);
m_UserMessages.Insert(msg->name, msg);
}
return true;
}
return false;
}
bool CSmmAPI::MsgCacheSuccessful()
{
return m_MsgCount > -1;
}
int CSmmAPI::GetUserMessageCount()
{
return m_MsgCount;
}
int CSmmAPI::FindUserMessage(const char *name, int *size)
{
int index = m_UserMessages.Find(name);
if (size && index > -1)
{
UserMessage *msg = m_UserMessages.Element(index);
*size = msg->size;
}
return index;
}
const char *CSmmAPI::GetUserMessage(int index, int *size)
{
if (m_MsgCount <= 0 || index < 0 || index >= m_MsgCount)
{
return NULL;
}
UserMessage *msg = m_UserMessages.Element(index);
if (size)
{
*size = msg->size;
}
return msg->name;
}

View File

@ -17,6 +17,15 @@
*/
#include "ISmmAPI.h"
#include <tier1/utldict.h>
struct UserMessage
{
int size;
const char *name;
};
typedef CUtlDict<UserMessage *, int> UserMsgDict;
typedef void (*CONPRINTF_FUNC)(const char *, ...);
@ -26,6 +35,7 @@ namespace SourceMM
{
public:
CSmmAPI();
~CSmmAPI();
public:
void LogMsg(ISmmPlugin *pl, const char *msg, ...);
public:
@ -41,10 +51,10 @@ namespace SourceMM
void UnregisterConCmdBase(ISmmPlugin *plugin, ConCommandBase *pCommand);
void ConPrint(const char *fmt);
void ConPrintf(const char *fmt, ...);
bool CacheSuccessful();
bool CmdCacheSuccessful();
bool RemotePrintingAvailable()
{
return CacheSuccessful();
return CmdCacheSuccessful();
}
virtual void GetApiVersions(int &major, int &minor, int &plvers, int &plmin);
virtual void GetShVersions(int &shvers, int &shimpl);
@ -58,6 +68,9 @@ namespace SourceMM
void *VInterfaceMatch(CreateInterfaceFn fn, const char *iface, int min=-1);
void EnableVSPListener();
int GetGameDLLVersion();
int GetUserMessageCount();
int FindUserMessage(const char *name, int *size=NULL);
const char *GetUserMessage(int index, int *size=NULL);
public:
bool CacheCmds();
void LoadAsVSP();
@ -65,11 +78,15 @@ namespace SourceMM
{
return m_VSP;
}
bool CacheUserMessages();
bool MsgCacheSuccessful();
private:
META_RES m_Res;
CONPRINTF_FUNC m_ConPrintf;
bool m_Cache;
bool m_CmdCache;
bool m_VSP;
int m_MsgCount;
UserMsgDict m_UserMessages;
};
};

12
sourcemm/GAMES.txt Normal file
View File

@ -0,0 +1,12 @@
Metamod: Source generally tries to support all Source engine based games. However, some API features
may only be supported by some games. This does not mean these features will not work on
games not listed here, but that games listed here are specifically known and tested to work.
The only Source-based game completely unsupported by Metamod:Source is Dark Messiah.
User Message API Functions - GetUserMessageCount(), FindUserMessage(), GetUserMessage()
- Counter-Strike: Source
- Day of Defeat: Source
- Half-Life 2: Deathmatch
- The Ship
- SourceForts

View File

@ -278,16 +278,41 @@ public: // Added in 1.4 (1:5)
* @return Interface version of the loaded IServerGameDLL.
*/
virtual int GetGameDLLVersion() =0;
/**
* @brief Returns the number of user messages in the GameDLL.
*
* @return Number of user messages, or -1 if SourceMM has failed to get user message list.
*/
virtual int GetUserMessageCount() =0;
/**
* @brief Returns the index of the specified user message.
*
* @param name User message name.
* @param size Optional pointer to store size of user message.
* @return Message index, or -1 on failure.
*/
virtual int FindUserMessage(const char *name, int *size=NULL) =0;
/**
* @brief Returns the name of the specified user message.
*
* @param index User message index.
* @param size Optional pointer to store size of user message.
* @return Message name, or NULL on failure.
*/
virtual const char *GetUserMessage(int index, int *size=NULL) =0;
};
/** Version history
* 1.1.0 bumped API to 1:0. The breaking changes occured in sourcehook and the plugin API.
* 1.1.0 bumped API to 1:0. The breaking changes occurred in sourcehook and the plugin API.
* 1.1.2 added API call for generating iface names.
* 1.2 added API more helper functions and new SourceHook version.
* 1.2.2 added API for printing to client console (with string formatting)
* 1.3 added new interface search API
* 1.4 added VSP listener API
* 1.4 added VSP listener and user message API
*/
#endif //_INCLUDE_ISMM_API_H

View File

@ -156,6 +156,32 @@ CON_COMMAND(meta, "Metamod:Source Menu")
CONMSG(" DLL Path: %s\n", g_BinPath.c_str());
CONMSG(" Interface: ServerGameDLL%03d\n", g_GameDllVersion);
// Display user messages
if (g_SmmAPI.MsgCacheSuccessful())
{
const char *msgname;
int msgsize;
int msgcount = g_SmmAPI.GetUserMessageCount();
if (msgcount > 0)
{
CONMSG(" User Messages: %-32.31s %-5s %-5s\n", "Name", "Index", "Size");
for (int i = 0; i < msgcount; i++)
{
msgname = g_SmmAPI.GetUserMessage(i, &msgsize);
CONMSG(" %-32.31s %-5d %-5d\n", msgname, i, msgsize);
}
CONMSG(" %d user message%s in total\n", msgcount, (msgcount > 1) ? "s" : "");
} else {
CONMSG(" User Messages: None\n");
}
} else {
CONMSG(" User Messages: Failed to get list of user messages\n");
}
return;
} else if (strcmp(command, "refresh") == 0) {
char full_path[255];

View File

@ -36,6 +36,14 @@ int GetLastError()
{
return errno;
}
extern "C" void __cxa_guard_acquire(void)
{
}
extern "C" void __cxa_guard_release(void)
{
}
#endif
bool GetFileOfAddress(void *pAddr, char *buffer, size_t maxlength)

View File

@ -32,6 +32,7 @@ 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);
SH_DECL_HOOK1_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, edict_t *);
SH_DECL_HOOK0(IServerGameDLL, GameInit, SH_NOATTRIB, false, bool);
bool DLLInit(CreateInterfaceFn engineFactory, CreateInterfaceFn physicsFactory, CreateInterfaceFn filesystemFactory, CGlobalVars *pGlobals);
bool DLLInit_Post(CreateInterfaceFn engineFactory, CreateInterfaceFn physicsFactory, CreateInterfaceFn filesystemFactory, CGlobalVars *pGlobals);
void DLLShutdown_handler();
@ -50,7 +51,7 @@ bool bInFirstLevel = true;
bool gParsedGameInfo = false;
bool bGameInit = false;
SourceHook::List<GameDllInfo *> gamedll_list;
SourceHook::CallClass<IServerGameDLL> *dllExec;
SourceHook::CallClass<IServerGameDLL> *g_GameDllPatch;
int g_GameDllVersion = 0;
void ClearGamedllList();
@ -145,12 +146,21 @@ bool DLLInit(CreateInterfaceFn engineFactory, CreateInterfaceFn physicsFactory,
//Initialize our console hooks
ConCommandBaseMgr::OneTimeInit(static_cast<IConCommandBaseAccessor *>(&g_SMConVarAccessor));
g_GameDllPatch = SH_GET_CALLCLASS(g_GameDll.pGameDLL);
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.");
}
if (!g_SmmAPI.CacheUserMessages())
{
/* Don't know of a mod that has stripped out user messages completely,
but perhaps should do something different here? */
LogMessage("[META] Warning: Failed to get list of user messages.");
LogMessage("[META] Warning: The 'meta game' command will not display user messages.");
}
const char *pluginFile = g_Engine.icvar->GetCommandLineValue("mm_pluginsfile");
if (!pluginFile)
@ -165,8 +175,6 @@ bool DLLInit(CreateInterfaceFn engineFactory, CreateInterfaceFn physicsFactory,
bInFirstLevel = true;
dllExec = SH_GET_CALLCLASS(g_GameDll.pGameDLL);
RETURN_META_VALUE(MRES_IGNORED, true);
}
@ -184,7 +192,7 @@ bool GameInit_handler()
bGameInit = true;
return true;
RETURN_META_VALUE(MRES_IGNORED, true);
}
bool DLLInit_Post(CreateInterfaceFn engineFactory, CreateInterfaceFn physicsFactory, CreateInterfaceFn filesystemFactory, CGlobalVars *pGlobals)
@ -524,10 +532,10 @@ void DLLShutdown_handler()
g_SMConVarAccessor.MarkCommandsAsGameDLL();
g_SMConVarAccessor.UnregisterGameDLLCommands();
SH_CALL(dllExec, &IServerGameDLL::DLLShutdown)();
SH_CALL(g_GameDllPatch, &IServerGameDLL::DLLShutdown)();
SH_RELEASE_CALLCLASS(dllExec);
dllExec = NULL;
SH_RELEASE_CALLCLASS(g_GameDllPatch);
g_GameDllPatch = NULL;
g_SourceHook.CompleteShutdown();
@ -724,7 +732,7 @@ void LevelShutdown_handler(void)
bool LevelInit_handler(char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background)
{
if (!g_SmmAPI.CacheSuccessful())
if (!g_SmmAPI.CmdCacheSuccessful())
{
LogMessage("[META] Warning: Failed to initialize Con_Printf. Defaulting to Msg().");
LogMessage("[META] Warning: Console messages will not be redirected to rcon console.");

View File

@ -112,4 +112,7 @@ extern int g_GameDllVersion;
extern bool bGameInit;
/** @brief Global CallClass for IServerGameDLL */
extern SourceHook::CallClass<IServerGameDLL> *g_GameDllPatch;
#endif //_INCLUDE_SOURCEMM_H