Raised This Month: $51 Target: $400
 12% 

KeyValue() not called for my entity


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
klippy
AlliedModders Donor
Join Date: May 2013
Location: Serbia
Old 12-22-2015 , 17:34   KeyValue() not called for my entity
Reply With Quote #1

Hello there.

I've stumbled upon a problem. This is actually my first time writing an AMXX module, but I don't think the mistake comes out from my lack of experience, but actually this might be a game bug.

My code:
EntityInit_amxx.cpp
PHP Code:
#include "sdk/amxxmodule.h"

#include "CConquestBase.h"
#include "chooker.h"


CHooker HookerClass;
CHooker *Hooker = &HookerClass;

typedef void(*ENTITYINIT)(struct entvars_s *);
typedef void(*Func_DispatchKeyValue)(struct edict_s *, KeyValueData *);
typedef ENTITYINIT(*Func_GetEntityInit)(char *);

Func_GetEntityInit pfnGetEntityInit NULL;
CFunc *funcDispatchKeyValue NULL;
CFunc *funcGetEntityInit NULL;

ENTITYINIT GetEntityInit_Hook(char *pClassName);
void DispatchKeyValue_Hook(edict_t *pentKeyvalueKeyValueData *pkvd);



void OnMetaAttach(void) {
#ifdef _WIN32
    
pfnGetEntityInit Hooker->MemorySearch<Func_GetEntityInit>("0x55,0x8B,0xEC,0x8B,0x45,*,0x50,0xE8,0x34,0xFD,0xFF,0xFF,0x83,0xC4,0x04,0x5D,0xC3", (void *)gpGlobalsFALSE);
#else
#error Platforms other than Win32 are not supported (yet).
#endif

    
funcGetEntityInit Hooker->CreateHook((void *)pfnGetEntityInit, (void *)GetEntityInit_HookTRUE);
    
funcDispatchKeyValue Hooker->CreateHook((void *)gpGamedllFuncs->dllapi_table->pfnKeyValue, (void *)DispatchKeyValue_HookTRUE);

    if (
funcGetEntityInit == NULL) {
        
SERVER_PRINT("Failed to create GetEntityInit() hook...\n");
    }
    if (
funcDispatchKeyValue NULL) {
        
SERVER_PRINT("Failed to create DispatchKeyValue() hook...\n");
    }
}

void OnMetaDetach(void) {
    
funcGetEntityInit->Restore();
}


LINK_ENTITY_TO_CLASS(conquest_baseCConquestBase);

ENTITYINIT GetEntityInit_Hook(char *pClassName) {
    
GET_ORIG_FUNC(func);

    if (
func->Restore()) {
        
ENTITYINIT init NULL;
        if (!
strcmp(pClassName"conquest_base")) {
            
init = (ENTITYINIT)&conquest_base;
        }
        else {
            
init pfnGetEntityInit(pClassName);
        }

        
func->Patch();
        return 
init;
    }
    
    
// Damn, here comes the crash (I think) :( But should never happen I guess
    
return NULL;
}

void DispatchKeyValue_Hook(edict_t *pentKeyvalueKeyValueData *pkvd) {
    
GET_ORIG_FUNC(func);

    if (
func->Restore()) {
        
gpGamedllFuncs->dllapi_table->pfnKeyValue(pentKeyvaluepkvd);

        if (
pentKeyvalue != NULL && !pentKeyvalue->free) {
            
char szOutput[256];
            if (!
stricmp(STRING(pentKeyvalue->v.classname), "conquest_base")) {
                
CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pentKeyvalue);
                if (
pEntity != NULL && pkvd->fHandled == FALSE) {
                    
pEntity->KeyValue(pkvd);
                }

                
sprintf_s(szOutputsizeof(szOutput), "class: %s key: %s value: %s handled: %i\n"pkvd->szClassNamepkvd->szKeyNamepkvd->szValuepkvd->fHandled);
                
SERVER_PRINT(szOutput);
            }
        }

        
func->Patch();
    }

CConquestBase.h
PHP Code:
#ifndef CCONQUESTBASE_H
#define CCONQUESTBASE_H

#include <cbase.h>
#include <extdll.h>
#include <meta_api.h>


class CConquestBase : public CBaseEntity {
private:
    
float m_range;
    
int m_team;
    
string_t m_somevalue;

public:
    
void Spawn(void) {
        
SERVER_PRINT("CConquestBase spawned with classname: ");
        
SERVER_PRINT(STRING(pev->classname));
        
SERVER_PRINT("!\n");
    }

    
void KeyValue(KeyValueData *pkvd) {
        
SERVER_PRINT("KeyValue called for CConquestBase!\n");
        if (!
stricmp(pkvd->szKeyName"cq_team")) {
            
m_team atoi(pkvd->szValue);
            
pkvd->fHandled TRUE;
        }
        else if (!
stricmp(pkvd->szKeyName"cq_range")) {
            
m_range atof(pkvd->szValue);
            
pkvd->fHandled TRUE;
        }
        else if (!
stricmp(pkvd->szKeyName"cq_prefixedvalue")) {
            
m_somevalue ALLOC_STRING(pkvd->szValue);
            
pkvd->fHandled TRUE;
        }
        else {
            
CBaseEntity::KeyValue(pkvd);
        }
    }
};




/* Linker can't find these functions */
const Vector g_vecZero;
BOOL CBaseEntity::FVisible(CBaseEntitypEntity) {
    return 
FALSE;
}
BOOL CBaseEntity::FVisible(const Vector &vecOrigin) {
    return 
FALSE;
}
CBaseEntity *CBaseEntity::GetNextTarget(void) {
    return (
CBaseEntity*)VARS(CREATE_ENTITY());
}
BOOL CBaseEntity::IsInWorld(void) {
    return 
TRUE;
}
void CBaseEntity::SetObjectCollisionBox(void) {

}
void CBaseEntity::Killed(entvars_t *pevAttackerint iGib) {

}
int CBaseEntity::TakeDamage(entvars_t *pevInflictorentvars_t *pevAttackerfloat flDamageint bitsDamageType) {
    return 
0;
}
int CBaseEntity::TakeHealth(float flHealthint bitsDamageType) {
    return 
0;
}
int CBaseEntity::Save(CSave &save) {
    return 
0;
}
int CBaseEntity::Restore(CRestore &restore) {
    return 
0;
}
int CBaseEntity::DamageDecal(int bitsDamageType) {
    return -
1;
}
void CBaseEntity::TraceAttack(entvars_t *pevAttackerfloat flDamageVector vecDirTraceResult *ptrint bitsDamageType) {

}
void CBaseEntity::TraceBleed(float flDamageVector vecDirTraceResult *ptrint bitsDamageType) {

}

#endif 
My real intention is to actually create more entity types. For instance, if you call pfnCreateNamedEntity with a non-registered classname (like "my_custom_ent"), the engine won't even create the entity - it has to be a "registered" classname, that is, you have to write an exported C function with the same name as entity type (that is what LINK_ENTITY_TO_CLASS from HLSDK does).
What I do with my code is hook GetEntityInit(char *pClassName) from engine, and check if classname is equal to my own custom entity, and return my own function if it is. I know for sure this works because CConquestBase::Spawn() gets called.

The problem is that CConquestBase::KeyValue(KeyValueData *) doesn't get called at all. I just don't get why! That is also the reason I hooked DispatchKeyValue (pfnKeyValue) - to check if there's anything wrong with parameters passed to that function. Apparently there's not.
My hooked function's code actually does exactly the same as the real function does - check if pEntity is null, and if it is not, calls KeyValue. If I let the game do it - CConquestBase::KeyValue doesn't get called, but when I do it on my own (as you can see in my code), it works flawlessly.

Any explanation for this?
klippy is offline
klippy
AlliedModders Donor
Join Date: May 2013
Location: Serbia
Old 12-23-2015 , 05:44   Re: KeyValue() not called for my entity
Reply With Quote #2

I found a solution, but I am not entirely happy with it because it is game dependent.
What happened is that the game DLL didn't actually call my CConquestBase::KeyValue, it called CBaseEntity::Save. The reason for that is that Counter-Strike developers have changed CBaseEntity class, and that actually changed the vtable. Instead of KeyValue being on index 3, it is 4 in CS. I fixed that just by insterting a dummy virtual function in HLSDK between CBaseEntity::KeyValue and CBaseEntity:: Precache.

I guess I'll just copy CBaseEntity declaration/definition from ReGameDLL_CS and use that.

Last edited by klippy; 12-23-2015 at 05:44.
klippy 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 10:35.


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