Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
DarthMan opened this issue Feb 13, 2022 · 5 comments

Comments

@DarthMan
Copy link
Contributor

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 :-)

@ShadowsAdi
Copy link
Contributor

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)
	}
}

@DarthMan
Copy link
Contributor Author

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.

@ShadowsAdi
Copy link
Contributor

ShadowsAdi commented Feb 17, 2022

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
Copy link
Contributor

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

@DarthMan
Copy link
Contributor Author

DarthMan commented Mar 1, 2022

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

Good job

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants