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:
parent
c7a31de575
commit
bf3d98f77c
@ -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;
|
||||
}
|
||||
|
@ -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
12
sourcemm/GAMES.txt
Normal 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
|
@ -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
|
||||
|
@ -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];
|
||||
|
@ -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)
|
||||
|
@ -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.");
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user