reapi icon indicating copy to clipboard operation
reapi copied to clipboard

Add natives to check if resources are precached (model, generic, sound, event) and the pfn engine forwards

Open DarthMan opened this issue 3 years ago • 5 comments

Hello. It would be cool if the pfnPrecache forwards could be added to ReAPI. I'm using fakemeta on a plugin to block precaching some game resources (that are taking up precache space but are not used on the map), to avoid the 512 limit. However, fakemeta doesn't detect resources that were precached by external AMXX plugins, and Orpheu is a bit buggy when it comes to hooking engine functions, probably because of the way ReHLDS was compiled. Adding natives to check if those resources are precached would also be cool, SourceMod already have natives for them, for example https://sourcemod.dev/#/halflife/function.IsModelPrecached I need this for an EntMod plugin, where I spawn entities with different keys and values. We can't guess all models precached by a map anyways, and if a player would spawn an entity with an unprecached model, the server would crash. So it would be really cool to have new natives added to check for precache, just as in SourceMod.

Thanks :-)

DarthMan avatar Feb 13 '22 08:02 DarthMan

Here is a plugin with an API:

#include <amxmodx>
#include <fakemeta>
#define PLUGIN "[Precache List]"
#define VERSION "1.0"
#define AUTHOR "Shadows Adi"

enum _:Data
{
	Resource[256],
	Type
}

enum _:Types
{
	TypeModel = 1,
	TypeSound,
	TypeGeneric
}

new Array:g_aResources
new g_iPrecachedNum[Types]

public plugin_precache()
{
	g_aResources = ArrayCreate(Data)

	register_forward(FM_PrecacheModel, "fw_PrecacheModel")
	register_forward(FM_PrecacheSound, "fw_PrecacheSound") 
	register_forward(FM_PrecacheGeneric, "fw_PrecacheGeneric") 
}

public plugin_init()
{
	register_plugin(PLUGIN, VERSION, AUTHOR)
}

public plugin_natives()
{
	register_native("precache_get_item", "native_get_item")
	register_native("precache_get_size", "native_get_size")
}

public fw_PrecacheModel(szModel[])
{
	new szTemp[Data]

	copy(szTemp[Resource], charsmax(szTemp[Resource]), szModel)
	szTemp[Type] = TypeModel

	ArrayPushArray(g_aResources, szTemp)

	g_iPrecachedNum[TypeModel]++
}

public fw_PrecacheSound(szSound[])
{
	new szTemp[Data]

	copy(szTemp[Resource], charsmax(szTemp[Resource]), szSound)
	szTemp[Type] = TypeSound
	ArrayPushArray(g_aResources, szTemp)

	g_iPrecachedNum[TypeSound]++
}

public fw_PrecacheGeneric(szResource[])
{
	new szTemp[Data]

	copy(szTemp[Resource], charsmax(szTemp[Resource]), szResource)
	szTemp[Type] = TypeGeneric
	ArrayPushArray(g_aResources, szTemp)

	g_iPrecachedNum[TypeGeneric]++
}

public plugin_end()
{
	ArrayDestroy(g_aResources)
}

// native precache_get_item(iNum, szItem[], iLen, iType)
public native_get_item(iPluginID, iParamNum)
{
	if(iParamNum != 4)
	{
		log_error(AMX_ERR_NATIVE, "%s Incorrect param num. Valid: iNum, szItem[], iLen, iType", PLUGIN)
		return -1
	}

	new szTemp[Data]
	new iNum = get_param(1)
	new iSize = get_param(3)
	new iType = get_param(4)

	do
	{
		ArrayGetArray(g_aResources, iNum, szTemp)
		iNum += 1
	} 
	while(iType != szTemp[Type])

	set_string(2, szTemp, iSize)
	return 1
}

// native precache_get_size(iType)
public native_get_size(iPluginID, iParamNum)
{
	return g_iPrecachedNum[get_param(1)]
}

Usage of API:

/* Sublime AMXX Editor v4.2 */

#include <amxmodx>

native precache_get_item(iNum, szItem[], iLen, iType)
native precache_get_size(iType)

enum
{
	TypeModel = 1,
	TypeSound,
	TypeGeneric
}

#define PLUGIN  "Test [Precache list]"
#define VERSION "1.0"
#define AUTHOR  "Shadows Adi"

public plugin_init()
{
	register_plugin(PLUGIN, VERSION, AUTHOR)
	
	register_concmd("debugs", "show_precache")
}

public show_precache(id)
{
	new iSize = precache_get_size(TypeModel)
	new szTemp[256]

	for(new i; i < iSize; i++)
	{
		precache_get_item(i, szTemp, charsmax(szTemp), TypeModel)

		log_to_file("debugs.log", "Model: %s", szTemp)
	}

	iSize = precache_get_size(TypeSound)

	for(new i; i < iSize; i++)
	{
		precache_get_item(i, szTemp, charsmax(szTemp), TypeSound)

		log_to_file("debugs.log", "Sound: %s", szTemp)
	}

	iSize = precache_get_size(TypeGeneric)

	for(new i; i < iSize; i++)
	{
		precache_get_item(i, szTemp, charsmax(szTemp), TypeGeneric)

		log_to_file("debugs.log", "Generic: %s", szTemp)
	}
}

ShadowsAdi avatar Feb 17 '22 15:02 ShadowsAdi

This can only detect resources precached by the engine, not from external plugins, because it is using Fakemeta. So if I precache something this plugin will not detect it.

DarthMan avatar Feb 17 '22 16:02 DarthMan

Try this one. You have the test plugin above.

#include <amxmodx>
#include <orpheu>
#include <orpheu_stocks>
#include <fakemeta>

#define PLUGIN "[Precache List]"
#define VERSION "1.1"
#define AUTHOR "Shadows Adi"

enum _:Data
{
	Resource[256],
	Type
}

enum _:Types
{
	TypeModel = 1,
	TypeSound,
	TypeGeneric
}

new Array:g_aResources
new Trie:g_tResources
new g_iPrecachedNum[Types]

public plugin_precache()
{
	g_aResources = ArrayCreate(Data)
	g_tResources = TrieCreate()

	OrpheuRegisterHook(OrpheuGetEngineFunction("pfnPrecacheGeneric", "PrecacheGeneric"), "fw_PrecacheGeneric", OrpheuHookPre)
	OrpheuRegisterHook(OrpheuGetEngineFunction("pfnPrecacheModel", "PrecacheModel"), "fw_PrecacheModel", OrpheuHookPre)
	OrpheuRegisterHook(OrpheuGetEngineFunction("pfnPrecacheSound", "PrecacheSound"), "fw_PrecacheSound", OrpheuHookPre)

	register_forward(FM_PrecacheModel, "fw_PrecacheModel")
	register_forward(FM_PrecacheSound, "fw_PrecacheSound") 
	register_forward(FM_PrecacheGeneric, "fw_PrecacheGeneric")
}

public plugin_init()
{
	register_plugin(PLUGIN, VERSION, AUTHOR)
}

public plugin_natives()
{
	register_native("precache_get_item", "native_get_item")
	register_native("precache_get_size", "native_get_size")
}

public fw_PrecacheModel(szModel[])
{
	new szTemp[Data]

	copy(szTemp[Resource], charsmax(szTemp[Resource]), szModel)
	szTemp[Type] = TypeModel

	if(!TrieKeyExists(g_tResources, szTemp[Resource]))
	{
		ArrayPushArray(g_aResources, szTemp)
		TrieSetString(g_tResources, szTemp[Resource], szTemp[Type])
		g_iPrecachedNum[TypeModel]++
	}
}

public fw_PrecacheSound(szSound[])
{
	new szTemp[Data]

	copy(szTemp[Resource], charsmax(szTemp[Resource]), szSound)
	szTemp[Type] = TypeSound

	if(!TrieKeyExists(g_tResources, szTemp[Resource]))
	{
		ArrayPushArray(g_aResources, szTemp)
		TrieSetString(g_tResources, szTemp[Resource], szTemp[Type])
		g_iPrecachedNum[TypeSound]++
	}
}

public fw_PrecacheGeneric(szResource[])
{
	new szTemp[Data]

	copy(szTemp[Resource], charsmax(szTemp[Resource]), szResource)
	szTemp[Type] = TypeGeneric

	if(!TrieKeyExists(g_tResources, szTemp[Resource]))
	{
		ArrayPushArray(g_aResources, szTemp)
		TrieSetString(g_tResources, szTemp[Resource], szTemp[Type])
		g_iPrecachedNum[TypeGeneric]++
	}
}


public plugin_end()
{
	ArrayDestroy(g_aResources)
	TrieDestroy(g_tResources)
}

// native precache_get_item(iNum, szItem[], iLen, iType)
public native_get_item(iPluginID, iParamNum)
{
	if(iParamNum != 4)
	{
		log_error(AMX_ERR_NATIVE, "%s Incorrect param num. Valid: iNum, szItem[], iLen, iType", PLUGIN)
		return -1
	}

	new szTemp[Data]
	new iNum = get_param(1)
	new iSize = get_param(3)
	new iType = get_param(4)

	do
	{
		ArrayGetArray(g_aResources, iNum, szTemp)
		iNum += 1
	} 
	while(iType != szTemp[Type])

	set_string(2, szTemp, iSize)
	return 1
}

// native precache_get_size(iType)
public native_get_size(iPluginID, iParamNum)
{
	return g_iPrecachedNum[get_param(1)]
}

Orpheu functions signatures ( engine version: https://github.com/dreamstalker/rehlds/actions/runs/1838133332 ):

PF_precache_generic_I:

{
    "name"      : "PF_precache_generic_I",
    "library"   : "engine",
    "arguments" : 
    [
        {
            "type" : "char *" 
        }
    ],
    "identifiers" : 
    [
		{
            "os"    : "windows",
            "value" : [0x55, 0x8B, "*", 0x83, "*", 0x50, 0xA1, 0xC8, 0x53, "*", 0x04, 0x33, 0xC5, 0x89, 0x45, 0xFC]
        },
		{
            "os"    : "linux",
            "value" : 0x4D5D0
        }
    ]
}

PF_precache_model_I:

{
    "name"      : "PF_precache_model_I",
    "library"   : "engine",
    "arguments" : 
    [
        {
            "type" : "char *" 
        }
    ],
    "identifiers" : 
    [
		{
            "os"    : "windows",
            "value" : [0x55, 0x8B, "*", 0x53, 0x56, 0x57, 0x8B, 0x7D, 0x08, 0x33, "*", 0x85, "*", 0x0F, 0x84, 0xE3]
        },
		{
            "os"    : "linux",
            "value" : 0x7B0F0
        }
    ]
}

PF_precache_sound_I:

{
    "name"      : "PF_precache_sound_I",
    "library"   : "engine",
    "arguments" : 
    [
        {
            "type" : "char *" 
        }
    ],
    "identifiers" : 
    [
		{
            "os"    : "windows",
            "value" : [0x55, 0x8B, "*", 0x56, 0x57, 0x8B, 0x7D, "*", 0x85, "*", 0x0F, 0x84, 0x84]
        },
		{
            "os"    : "linux",
            "value" : 0x7B3C0
        }
    ]
}

ShadowsAdi avatar Feb 17 '22 19:02 ShadowsAdi

If you're still interested, I've just made this plugin which catches all resources precached: https://github.com/ShadowsAdi/PrecacheList

ShadowsAdi avatar Feb 27 '22 01:02 ShadowsAdi

If you're still interested, I've just made this plugin which catches all resources precached: https://github.com/ShadowsAdi/PrecacheList

Good job

DarthMan avatar Mar 01 '22 18:03 DarthMan