Raised This Month: $32 Target: $400
 8% 

[HOWTO] Make a nice say-hook


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
PM
hello, i am pm
Join Date: Jan 2004
Location: Canalization
Old 01-05-2005 , 15:09   [HOWTO] Make a nice say-hook
Reply With Quote #1

I don't know whether you guys already know this, but I found out how to make a nice, pretty mod-independent say command hook.
As you have probably noticed, doing a CON_COMMAND_F("say", FCVAR_GAMEDLL) makes the original command completly disappear. So, this is what I came up with:

Code:
// Special hook for say commands
class CSayHook : public ConCommand
{
	// This will hold the pointer original gamedll say command
	ConCommand *m_pGameDLLSayCommand;
public:
	CSayHook() : ConCommand("say", NULL, "say messages", FCVAR_GAMEDLL), m_pGameDLLSayCommand(NULL)
	{ }

	// Override Init
	void Init()
	{
		// Try to find the gamedll say command
		ConCommandBase *pPtr = g_pICvar->GetCommands();
		while (pPtr)
		{
			if (pPtr != this && pPtr->IsCommand() && strcmp(pPtr->GetName(), "say") == 0)
				break;
			// Nasty
			pPtr = const_cast<ConCommandBase*>(pPtr->GetNext());
		}
		if (!pPtr)
			OMGOMGOMG ! PRINT ERROR1!1!1!!!!

		m_pGameDLLSayCommand = pPtr;

		// Call base class' init function
		ConCommand::Init();
	}

	void Dispatch()
	{
		// Do the normal stuff, return if you want to override the say

		// Forward to gamedll
		m_pGameDLLSayCommand->Dispatch();
	}
};

// Don't forget to make an instance
CSayHook g_SayHook;
(almost copy & paste from sourcemod)


EDIT: Fixed. Forgot m_pGameDLLSayCommand = pPtr; Thanks to u/d/theqizmo for reporting this
__________________
hello, i am pm
PM is offline
blaubaer
Junior Member
Join Date: Nov 2004
Location: Germany - Berlin
Old 01-05-2005 , 15:38  
Reply With Quote #2

nice ! ;)
blaubaer is offline
Send a message via ICQ to blaubaer Send a message via AIM to blaubaer Send a message via MSN to blaubaer Send a message via Yahoo to blaubaer
Manip
Senior Member
Join Date: Jan 2004
Old 01-05-2005 , 16:09  
Reply With Quote #3

..wow... How over-programmed ..

Here is mine:

Code:
CON_COMMAND_F( say, "Hooks say", FCVAR_GAMEDLL )
{
}
Manip is offline
Send a message via AIM to Manip
BeetleFart
SourceMod Donor
Join Date: Apr 2004
Old 01-05-2005 , 16:26  
Reply With Quote #4

Quote:
Originally Posted by Manip
..wow... How over-programmed ..

Here is mine:

Code:
CON_COMMAND_F( say, "Hooks say", FCVAR_GAMEDLL )
{
}
but that doesnt dispatch the say messages to the clients
BeetleFart is offline
vancelorgin
Senior Member
Join Date: Dec 2004
Location: san frandisco
Old 01-06-2005 , 06:03  
Reply With Quote #5

I like the concept. Like I said I was trying to do this with slot commands but those are apparently done differently. I don't, however, use it for say. The code for the say command is right in the sdk .

Code:
const ConVar* mp_teamplay = NULL; 

void Handle_Say(int iMode){
	if(!mp_teamplay)
		mp_teamplay = cvar->FindVar("mp_teamplay");

	if(!mp_teamplay)
		return;

	edict_t* pEdict = engine->PEntityOfEntIndex(g_nCommandClientIndex + 1);

	CPlayer* pPlayer = CPlayer::Find(pEdict);

	int j;
	char* p;
	char text[AMP_MAX_LINE_LEN];
	char szTemp[AMP_MAX_LINE_LEN];
	
	const char* pcmd = engine->Cmd_Argv(0);
	int argc = engine->Cmd_Argc();

	if(argc == 0)
		return;

	if(!stricmp(pcmd, "say") || !stricmp(pcmd, "say_team") || !stricmp(pcmd, "say_admin"))
		if(argc >= 2)
			p = (char *)engine->Cmd_Args();
		else
			return; // say with a blank message, nothing to do
	
	else{  // Raw text, need to prepend argv[0]
		if(argc >= 2)
			_snprintf(szTemp,sizeof(szTemp), "%s %s", (char*)pcmd, (char*)engine->Cmd_Args());
		else
			_snprintf(szTemp,sizeof(szTemp), "%s", (char*)pcmd); // Just a one word command, use the first word...sigh
		
		p = szTemp;
	}

	p = CheckChatText(p); // make sure the text has vlaid content

	if(!p)
		return;

	if(*p == CONTROLCHAR && pPlayer)
		if(cmds.Execute(p, pEdict, true))
			return;
		else{
			msgs.SayTo(pEdict, "Invalid command \n");

			return;
		}

	const char* pszPrefix = NULL;

	if(iMode == 1)
		pszPrefix = "(TEAM) ";
	else if(iMode == 2)
		pszPrefix = "(ADMIN) ";

	if(pszPrefix && strlen( pszPrefix ) > 0)
		_snprintf(text, sizeof(text), "%s %s: ", pszPrefix, pPlayer? pPlayer->GetName() : "Server");
	else
		_snprintf(text, sizeof(text), "%s: ", pPlayer? pPlayer->GetName() : "Server");

	j = sizeof(text) - 2 - strlen(text);  // -2 for /n and null terminator
	
	if((int)strlen(p) > j)
		p[j] = 0;

	strncat(text, p, sizeof(text));

	if(iMode == 2){
		//admins only - player access levels are still being implimented
	}else
		msgs.SayFrom(pEdict, text, (mp_teamplay->GetInt() > 1));
}

CON_COMMAND_F(say, "Sends shit to everyone.", FCVAR_GAMEDLL){
	Handle_Say(0);
}

CON_COMMAND_F(say_team, "Sends shit to your team.", FCVAR_GAMEDLL){
	Handle_Say(1);
}

CON_COMMAND_F(say_admin, "Sends shit other nimdas.", FCVAR_GAMEDLL){
	Handle_Say(2);
}

COMMAND_(me, 0, "emote [<3 ltfxdude :]"){
	if(!pPlayer)
		return;

	std::string strText = VASTR(pPlayer->GetName());

	strText += " ";

	strText += cmds.Args();

	msgs.Say((char*)strText.c_str());
}
Thanks to my command manager all my commands are available through chat too. Woo-hah :/
__________________
Avoid like the plague.
vancelorgin is offline
PM
hello, i am pm
Join Date: Jan 2004
Location: Canalization
Old 01-06-2005 , 07:43  
Reply With Quote #6

Heh, I didn't want to copy the SDK code into my plugin - this seems to be far more mod-independent and easier
(I also didn't want to use offset hacks)

Manip: Like BeetleFart said, you would have to recode the whole say thing in order to dispatch the message to all the clients, like vancelorgin did.
__________________
hello, i am pm
PM is offline
XAD
Senior Member
Join Date: Mar 2004
Location: Sweden
Old 01-06-2005 , 13:06  
Reply With Quote #7

Quote:
Originally Posted by PM
Heh, I didn't want to copy the SDK code into my plugin - this seems to be far more mod-independent and easier
(I also didn't want to use offset hacks)
I agree that if you don't need use "engine-hooks" you shouldn't (and I like your implementation) but the offset hack is not that bad as it is based on the "official" engine API. If Valve would change the engine API by reordering any virtual functions then all existing mods would stop working (which is not realistic)... They will do it the same way as in CS1.6, by adding new functions at the end which won't change any existing function offsets... Remember that anyone creating a mod will also use the offset in the current interface eventhough it's calculated by the compiler...

/X
XAD is offline
BAILOPAN
Join Date: Jan 2004
Old 01-06-2005 , 17:13  
Reply With Quote #8

You underestimate valve. They would readily break API to promote something different (just look at the past).

Mod and engine independence is a huge plus no matter how cool hacked code is ;]
__________________
egg
BAILOPAN is offline
XAD
Senior Member
Join Date: Mar 2004
Location: Sweden
Old 01-06-2005 , 19:15  
Reply With Quote #9

Quote:
Originally Posted by BAILOPAN
You underestimate valve. They would readily break API to promote something different (just look at the past).

Mod and engine independence is a huge plus no matter how cool hacked code is ;]
Well if you think about eiface change, that was a misstake by MetaMod (if it should have set the function pointer struct to zeroes when allocating it and before sending it to the mods there would have not been any need to change the mods)...

The engine is mod independent, it's the g#d d¤%m engine ()??!!
All HL2DS mods are engine API dependent or otherwise it would NOT be a HL2DS based game!!?? If the engine API would change that much (that the offset "hack" wouldn't work) then you still would have to recompile all mods and server plugins... so what's the difference (except that you would have to re-create a new function pointer struct when using offsets)??

Remember that valve is in the business of getting money from licenses of their engine. If they would change it (the engine API) so it would require recompiles of mods then you would have a lot of unhappy licensors (ie BIG customers). The last engine API changes on HLDS were done to support CS:CZ and it was only new functions appended to the end of the function pointer list... which won't cause a problem when using offsets and should have not been an issue in the past if MetaMod would have been implemented "correctly"...

Unless it will relink the objects internal virtual functions at runtime??

BUT again... everyone is, and will, choose their own way to implement their solutions (unless you are on a development team )...

/X
XAD is offline
vancelorgin
Senior Member
Join Date: Dec 2004
Location: san frandisco
Old 01-06-2005 , 20:10  
Reply With Quote #10

The offsets, bailo, PM is referring to, are offsets in the Interfaces' virtual function tables - publicly accessible, mod universal, fundamental classes, like XAD said. When you hear 'offset' you instantly think hardcoded address - which is a terrible mistake I never hardcode addresses in my projects. Offsets are unnecessary here (in fact I don't see how you'd even be able to squeeze them in somewhere). Besides, the modified standard class / interface method can only be used in rare situations where the plugin is called or given a chance to inject itself somewhere. Vtable hooks can be used anywhere. Just try to overload IVEngineServer without it :/

The 'offset' method XAD and I are using is just as mod universal as the server plugin interface, nay, the engine interface. Unless a mod releases it's own engine binaries, the method will work flawlessly.


Though I use this method elsewhere, I prefer mine for say hooking, for the time being, as it allows me to add say text tokens, like @n for name, @l for location, @h for health, etc, and otherwise modify what the client says. Doing that with your method would require somehow overwriting the engine's argument list, which would be, by your standards, ugly ^_^
__________________
Avoid like the plague.
vancelorgin is offline
Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 22:51.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode