VScript icon indicating copy to clipboard operation
VScript copied to clipboard

Global arrays/tables + finding entity scope

Open Kerouha opened this issue 5 months ago • 10 comments

I'd like to know if following is possible with this plugin:

  • Getting an arbitrary array/table from global scope
  • Determining an entity, from which scope a detoured function was called

If possible, it would be nice to have an example included either in readme or test plugin.

Kerouha avatar Jul 28 '25 11:07 Kerouha

HSCRIPT_RootTable has all of the global variables provided that you can use.

As for determining an entity, which function are you referring to? For a game's function its already provided at readme with the entity param, but if you want to "detour" a script function from vscript itself, it's currently not possible to detour it.

FortyTwoFortyTwo avatar Jul 28 '25 17:07 FortyTwoFortyTwo

  1. So how can one read an array from global table? For example, if one is defined during game: ::_array <- [0, 30, 0, 0, 60]
  2. I mean scenario like this: Create an empty global function with this plugin and detour it. Call said function during game DoEntFire("some_ent", "CallScriptFunction", "MyEmptyFunc", 0.0, null, self); Is it possible to determine/reference "calling" entity? Similar to using self inside vscript function.

Kerouha avatar Jul 31 '25 09:07 Kerouha

HSCRIPT_RootTable.GetValue("_array") allows you to get the array object itself, however I don't think it's possible to get the individual values as HSCRIPT methodmap only support retrieving values from a table rather than an array, since vscript <-> game code only uses table to communicate eachother.


I'm not sure if CallScriptFunction supports directly calling a global function. If it does work, then there is a hack valve had put in which you may be able to make use of from HSCRIPT_RootTable.GetValue("owninginstance").

Otherwise I'm not sure what use case you'd have into creating a global function and needing a calling entity, which i'd expect VScriptClass.CreateFunction to be used instead so you can directly get the "this" entity thats being used. I suppose that should've been mentioned in readme.

FortyTwoFortyTwo avatar Jul 31 '25 10:07 FortyTwoFortyTwo

Ok, so creating an empty class function worked in my case.

In L4D2, CallScriptFunction can call global functions, at least when initiated from vscript:

DoEntFire("!self", "CallScriptFunction", "SomeGlobalFunction", 0.0, null, ent);
EntFire( "worldspawn", "CallScriptFunction", "SomeGlobalFunction", 0.1 );

Kerouha avatar Aug 01 '25 23:08 Kerouha

About class functions, I'm not sure when/where one should create and register these. According to #7, we probably can't do it on OnScriptVMInitialized/OnAllPluginsLoaded

I've tried creating them a frame after OnMapStart. They may not appear if it's the first time level has loaded.

static VScriptFunction g_vsTeleport;

public void OnMapStart()
{
	RequestFrame(CreateVScriptClassFunctions);
}

void CreateVScriptClassFunctions()
{
	PrintToServer("CreateVScriptClassFunctions");
	g_vsTeleport = VScript_CreateClassFunction("CBaseEntity", "DoTeleport");
	g_vsTeleport.SetParam(1, FIELD_VECTOR);
	g_vsTeleport.Return = FIELD_BOOLEAN;
	g_vsTeleport.SetFunctionEmpty();
	g_vsTeleport.CreateDetour().Enable(Hook_Post, Detour_Teleport);
	g_vsTeleport.Register();
}

public MRESReturn Detour_Teleport(int iEntity, DHookReturn hReturn, DHookParam hParam)
{
	static float vPos[3];
	//hParam.GetObjectVarVector(1, 0, ObjectValueType_VectorPtr, vPos); // btw shouldn't this work?
	PrintToServer("Detour_Teleport, it's crashin' time!! %d", EntIndexToEntRef(iEntity));
	for (int i = 0; i < sizeof(vPos); i++)
		vPos[i] = hParam.GetObjectVar(1, i * 4, ObjectValueType_Float);
	TeleportEntity(iEntity, vPos, NULL_VECTOR, NULL_VECTOR);
	hReturn.Value = true;
	return MRES_Supercede;
}
] script printl(Ent(75).DoTeleport)

AN ERROR HAS OCCURED [the index 'DoTeleport' does not exist]

CALLSTACK
*FUNCTION [main()] unnamed line [1]

LOCALS
[vargv] ARRAY
[this] TABLE

Even if you reload the level, server/game will crash when calling this function anyways. Is it because of a vector?

[DHOOKS] FATAL: Failed to find return address of original function. Check the arguments and return type of your detour setup.

Kerouha avatar Aug 20 '25 12:08 Kerouha

Since a class has been modified with its new function, VScript_ResetScriptVM need to be called afterward for the function to be used.

For the crash, does running the vscript_test plugin work for you without issue? It has a BunchOfParams function test with vector usage, but i suspect the pre-post hook usage may work differently between it that may cause the crash

FortyTwoFortyTwo avatar Aug 20 '25 14:08 FortyTwoFortyTwo

Quick test attempt to load vscript_test.smx crashed both game in singleplayer, and dedicated server. I use vscript.smx v1.9.1.87

Also from experience, resetting script VM tends to break vscript mods (and allegedly is problematic on Linux) so I'd avoid doing it.

Kerouha avatar Aug 20 '25 14:08 Kerouha

Crash is probably related to #7 then.

Resetting script VM can indeed mess things up a bit, where ideally it should only be run during map start or as soon as possible. Otherwise you'll have to create a function, then start a new map for the script VM to reset itself

FortyTwoFortyTwo avatar Aug 20 '25 16:08 FortyTwoFortyTwo

If possible, would be nice to have something like VScript_OnRegisterClass(const char[] className)

For now, I guess I'll redo Entity.DoTeleport(vecPos) into global TeleportEntity(iEnt, fPosX, fPosY, fPosZ)

Kerouha avatar Aug 20 '25 16:08 Kerouha

Continuing on vectors theme, is this just not possible for now?

Action CmdTestPathW(int client, int args)
{
	static float vPos[3], fDistance;
	static int iTarget, iSprite;
	
	iTarget = GetCmdArgInt(1);
	fDistance = GetCmdArgFloat(2);
	if (!iTarget)
		iTarget = IsDedicatedServer() ? client : 1;
	if (~g_iInit & INIT_VS_PATHW) InitPathWithin();
	
	SDKCall(g_vsPathWithin, VScript_EntityToHScript(iTarget), fDistance == 0.0 ? 250.0 : fDistance, vPos);
	
	ReplyToCommand(client, "%.2f %.2f %.2f", vPos[0], vPos[1], vPos[2]);
	iSprite = MakeEnvSprite(vPos, _, "vgui/hud/autoaim.vmt", 0.5, 5);
	CreateTimer(g_fSpriteTime, RemoveEnt, iSprite);
	return Plugin_Handled;
}

void InitPathWithin()
{
	VScriptFunction func = VScript_GetClassFunction("CTerrorPlayer", "TryGetPathableLocationWithin");
	g_vsPathWithin = func.CreateSDKCall();
	g_iInit |= INIT_VS_PATHW;
}

Result always comes up as 0.0 0.0 0.0. Same when trying to call CBaseAnimating's GetBoneOrigin

Kerouha avatar Aug 23 '25 07:08 Kerouha