PDA

View Full Version : [TF2 & L4D & L4D2] Actions


BHaType
02-15-2022, 07:11
ConVars
// Logs every action transitions
// 1 - Enable debug, 0 - Disable debug
ext_actions_debug "0"

// Logs every action/behavior/component allocation or deallocation
// Used to track memory leaks
// 1 - Enable memory debug, 0 - Disable memory debug
ext_actions_debug_memory "0"


Commands
ext_actions_dump - Dumps every nextbot actions to console.

Action Handlers

You can hook action handler by setting it's callback function (or unhook with INVALID_FUNCTION)

action.OnUpdate = OnUpdate;


Simple example, if you need to hook "Update" handler of every "whatever" action

/*
* OnActionCreated notifies whenever entity got a new action but without actual transition yet.
* That means action is constructed by the corresponding constructor and still hasn't been updated or * started yet.
*/
public void OnActionCreated(BehaviorAction action, int actor, const char[] name)
{
if (strcmp(name, "whatever") = 0)
{
action.Update = OnWhateverUpdate;
}
}

public Action OnWhateverUpdate(BehaviorAction action, int actor, float interval, ActionResult)
{
// unhook it
action.Update = INVALID_FUNCTION;
return Plugin_Continue;
}


Every action handler has it's own callback prototype and you must use the right one
All action handler function prototypes https://github.com/Vinillia/actions.ext/blob/87e58e09678e5355e7ab32c50b28d85a10c9fc9a/sourcemod/include/actions_processors.inc#L74-L183

Getting"whatever" action of specific entity


BehaviorAction action = ActionsManager.GetAction(entity, "whatever");
if (action != INVALID_ACTION)
{
//
}


Action Userdata

You can store any data in action with and without identity

Data stored without identity is shared between plugins and persists until action is deleted
Data stored with identity is NOT shared between plugins (multiple plugins can use same keys) and persists until plugin who owns the data is unloaded

Usually you want to store data without identity, the latter is for specific use cases where you want to store handles or something important that has to be accessible even if action is destroyed

Example:

public void OnActionCreated(BehaviorAction action, int actor, const char[] name)
{
// to store with identity add "Identity" after Data
// action.SetUserDataIdentity("my_int", 2);
// action.SetUserDataIdentityVector("my_vector", { -89.0, 125.0, -5324.0 });

action.SetUserData("my_int", 2);
action.SetUserData("my_float", 2.25);
action.SetUserDataVector("my_vector", { -89.0, 125.0, -5324.0 });
action.SetUserDataString("my_string", "Test test test test");
}

public void OnActionDestroyed(BehaviorAction action, int actor, const char[] name)
{
int ivalue, fvalue;
float vector[3];
char buffer[64];

ivalue = action.GetUserData("my_int");
fvalue = action.GetUserData("my_float");
action.GetUserDataVector("my_vector", vector);
action.GetUserDataString("my_string", buffer, sizeof buffer);

PrintToServer("ivalue: %i", ivalue);
PrintToServer("fvalue: %f", fvalue);
PrintToServer("vector: {%f %f %f}", vector[0], vector[1], vector[2]);
PrintToServer("buffer: %s", buffer);
}


Action Constructor

If you want to create internal game actions then you need to create SDKCall then call it on allocated memory.
Even if this approach works, I still think there should be a way to hide SDKCall and memory allocation into gamedata.

So there is ActionConstructor


ActionConstructor g_SampleActionConstructor;

public void OnPluginStart()
{
GameData data = new GameData("");

g_SampleActionConstructor = ActionConstructor.SetupFromConf(data, "SampleAction");

delete data;

int entity = FindEntityByClassname(-1, "player");
g_SampleActionConstructor.Execute(entity);
}



"Games"
{
"left4dead2"
{
"ActionConstructors"
{
"SampleAction"
{
/* Signature/address must point to action constructor */
// "signature" "whatever_signature"
// "address" "whatever_address"

/* Size of desired action */
// "size" "123"
// "size" "0x123"
// "size" "123h"

"params"
{
"target"
{
/*
* basic - plain generic data
* float - floating point data
* object - raw struct/class/array?
*/
"type" "basic"

/*
* byval - pass by val
* byref - pass by ref
* dtor - object has destructor
* ctor - object has constructor
* assignop - object has assignment operator
* unalign - object contains unaligned fields
*/
"flags" "byval"

// You can specify encoder to transform arbitary data to valve params (eg. entity index -> CBaseEntity)
/*
* entity - entity index to CBaseEntity
* vector - raw array to Vector struct
*/
"encoder" "entity"
}
}
}
}
}
}


Action Component


All actions are eventually stored in the behavior and behavior is stored in IIntention component.
You can create your own IIntention component called "ActionComponent"
The created component works exactly the same as the other components but you can also delete it at any time
If the plugin is unloaded all its components will be deleted automatically


#include <actions>

public void OnPluginStart()
{
HookEvent("player_spawn", player_spawn);
}

void player_spawn(Event event, const char[] name, bool dontBroadcast)
{
int client = GetClientOfUserId(event.GetInt("userid"));

if (!client || !IsClientInGame(client))
return;

PrintToChatAll("player_spawn");

ActionComponent component = new ActionComponent(client,
SampleComponent_InitialAction,
SampleComponent_Update,
SampleComponent_Upkeep,
SampleComponent_Reset,
"SampleComponent");

CreateTimer(10.0, timer_delete_component, component, TIMER_FLAG_NO_MAPCHANGE);
}

void timer_delete_component(Handle timer, ActionComponent component)
{
delete component;
LogMessage("Deleted component");
}

void SampleComponent_Update(ActionComponent component, int entity)
{
LogMessage("SampleComponent_Update");
}

void SampleComponent_Upkeep(ActionComponent component, int entity)
{
LogMessage("SampleComponent_Upkeep");
}

void SampleComponent_Reset(ActionComponent component, int entity)
{
LogMessage("SampleComponent_Reset");
}

BehaviorAction SampleComponent_InitialAction(ActionComponent component, int entity)
{
BehaviorAction action = ActionsManager.Create("MySampleAction");

action.OnStart = MySampleAction_OnStart;

return action;
}

Action MySampleAction_OnStart(any action, int actor, float interval, ActionResult result)
{
LogMessage("MySampleAction_OnStart");
return Plugin_Continue;
}



#pragma semicolon 1
#pragma newdecls required

#include <sourcemod>
#include <sdktools>

#include <actions>

ActionConstructor g_hApproachVector;
ActionConstructor g_hApproachEntity;

methodmap SurvivorApproach < BehaviorAction
{
public static bool ApproachVector(int bot, const float goal[3])
{
BehaviorAction action = GetBestSuspendebale(bot);

if (action == INVALID_ACTION)
return false;

action.SetUserDataVector("approach_goal", goal);
action.Update = Suspendable_Update;
}

public static bool ApproachEntity(int bot, int entity)
{
BehaviorAction action = GetBestSuspendebale(bot);

if (action == INVALID_ACTION)
return false;

action.SetUserData("approach_target", EntIndexToEntRef(entity));
action.Update = Suspendable_Update;
}
}

public Action Suspendable_Update(BehaviorAction action, int actor, float interval, ActionResult result)
{
action.Update = INVALID_FUNCTION;

int approach_target;
if (ActionsManager.GetActionUserData(action, "approach_target", approach_target))
{
if ((approach_target = EntRefToEntIndex(approach_target)) <= 0)
return Plugin_Continue;

return action.SuspendFor(SetupAndReturn(g_hApproachE ntity, approach_target));
}
else
{
float goal[3];
action.GetUserDataVector("approach_goal", goal);
return action.SuspendFor(SetupAndReturn(g_hApproachV ector, .goal = goal));
}
}

BehaviorAction SetupAndReturn(ActionConstructor constructor, int target = 0, const float goal[3] = NULL_VECTOR)
{
BehaviorAction action = INVALID_ACTION;

if (constructor == g_hApproachVector)
{
action = g_hApproachVector.Execute(goal);
action.SetUserDataVector("survivor_approach_goal", goal);
}
else
{
action = g_hApproachEntity.Execute(target);
action.SetUserData("survivor_approach_target", EntIndexToEntRef(target));
}

action.OnUpdate = SurvivorApproach_Update;
return action;
}

public Action SurvivorApproach_Update(BehaviorAction action, int actor, float interval, ActionResult result)
{
float origin[3], goal[3];
GetClientAbsOrigin(actor, origin);

if (!ActionsManager.GetActionUserDataVector(acti on, "survivor_approach_goal", goal))
{
int target = EntRefToEntIndex(action.GetUserData("survivor_approach_target"));

if (target <= 0)
{
return action.Done();
}

GetEntPropVector(target, Prop_Send, "m_vecOrigin", goal);
}

float distance = GetVectorDistance(origin, goal, true);
if (distance < 150.0 * 150.0)
{
return action.Done();
}

return action.Continue();
}

public void OnPluginStart()
{
GameData data = new GameData("l4d2_bot_move");

g_hApproachVector = ActionConstructor.SetupFromConf(data, "SurvivorLegsApproach::SurvivorLegsApproachVec tor");
g_hApproachEntity = ActionConstructor.SetupFromConf(data, "SurvivorLegsApproach::SurvivorLegsApproachEnt ity");

delete data;

RegAdminCmd("sm_bot_move_entity", sm_bot_move_entity, ADMFLAG_ROOT);
RegAdminCmd("sm_bot_move_vector", sm_bot_move_vector, ADMFLAG_ROOT);
}

Action sm_bot_move_vector(int client, int args)
{
if (args < 1)
{
ReplyToCommand(client, "Usage: sm_bot_move_vector <bot>");
return Plugin_Handled;
}

float origin[3], angle[3];
float goal[3];

GetClientEyePosition(client, origin);
GetClientEyeAngles(client, angle);

TR_TraceRayFilter(origin, angle, MASK_SHOT, RayType_Infinite, TraceFilter);

if (!TR_DidHit())
{
ReplyToCommand(client, "TraceRayFilter didn't hit");
return Plugin_Handled;
}

TR_GetEndPosition(goal);

char sArg[32], target_name[MAX_TARGET_LENGTH];
GetCmdArg(1, sArg, sizeof(sArg));

int target_list[MAXPLAYERS], target_count;
bool tn_is_ml;

if ((target_count = ProcessTargetString(
sArg,
client,
target_list,
MAXPLAYERS,
COMMAND_FILTER_ALIVE, /* Only allow alive players */
target_name,
sizeof(target_name),
tn_is_ml))
<= 0)
{
ReplyToTargetError(client, target_count);
return Plugin_Handled;
}

for (int i = 0; i < target_count; i++)
{
if (IsFakeClient(target_list[i]))
SurvivorApproach.ApproachVector(target_list[i], goal);
}

return Plugin_Handled;
}

Action sm_bot_move_entity(int client, int args)
{
if (args < 1)
{
ReplyToCommand(client, "Usage: sm_bot_move_entity <bot>");
return Plugin_Handled;
}

int target = GetClientAimTarget(client, false);

if (target == -1)
{
ReplyToCommand(client, "Invalid target entity");
return Plugin_Handled;
}

char sArg[32], target_name[MAX_TARGET_LENGTH];
GetCmdArg(1, sArg, sizeof(sArg));

int target_list[MAXPLAYERS], target_count;
bool tn_is_ml;

if ((target_count = ProcessTargetString(
sArg,
client,
target_list,
MAXPLAYERS,
COMMAND_FILTER_ALIVE, /* Only allow alive players */
target_name,
sizeof(target_name),
tn_is_ml))
<= 0)
{
ReplyToTargetError(client, target_count);
return Plugin_Handled;
}

for (int i = 0; i < target_count; i++)
{
if (IsFakeClient(target_list[i]))
SurvivorApproach.ApproachEntity(target_list[i], target);
}

return Plugin_Handled;
}

stock BehaviorAction GetBestSuspendebale(int bot)
{
BehaviorAction action = ActionsManager.GetAction(bot, "SurvivorLegsApproach");

if (action == INVALID_ACTION)
action = ActionsManager.GetAction(bot, "SurvivorLegsStayClose");

if (action != INVALID_ACTION)
return action;

action = ActionsManager.GetAction(bot, "SurvivorBehavior");

if (action == INVALID_ACTION)
return INVALID_ACTION;

if (action.IsSuspended)
{
action = action.Above;
}
else
{
action = action.Child;
}

return action;
}

bool TraceFilter(int entity, int contentsMask)
{
return entity <= 0;
}

gamedata (https://pastebin.com/5nRYfk1L)

Source code (https://github.com/Vinillia/actions.ext.git)

Gold Fish
02-16-2022, 08:45
What are the "action" related to? Any actions of survivors and infected (players)?

BHaType
02-16-2022, 20:05
What are the "action" related to? Any actions of survivors and infected (players)?

"Actions" are only related to nextbots. I can't give you full list of game actions since there are to many of them

https://developer.valvesoftware.com/wiki/Example_of_NextBot_Behaviors

Updated

- Fixed InitialContainedAction handler for custom actions

Gold Fish
02-17-2022, 03:05
"Actions" are only related to nextbots. I can't give you full list of game actions since there are to many of them

https://developer.valvesoftware.com/wiki/Example_of_NextBot_Behaviors

Updated

- Fixed InitialContainedAction handler for custom actions


I understand that with the help of your extension, can force a tank to attack a certain player or make him just stand still or run away from players or even make zombie friends for a survivors, I understand that the extension gives full control over the intelligence of entities that is embedded in the game engine?)

Lux
02-17-2022, 04:01
This is a blessing thanks for release

HarryPotter
02-19-2022, 16:00
If I write

#pragma semicolon 1
#pragma newdecls required

#include <actions>


then compiler will show error

//// l4d2_bile_out_nav_negate_action.sp
//
// D:\Program Files (x86)\Steam\steamapps\common\Left 4 Dead 2 Dedicated Server\left4dead2\addons\sourcemod\scripting 1.10\include\actions.inc(37) : error 001: expected token: ";", but found "forward"
// D:\Program Files (x86)\Steam\steamapps\common\Left 4 Dead 2 Dedicated Server\left4dead2\addons\sourcemod\scripting 1.10\include\actions.inc(39) : error 001: expected token: ";", but found "methodmap"
// D:\Program Files (x86)\Steam\steamapps\common\Left 4 Dead 2 Dedicated Server\left4dead2\addons\sourcemod\scripting 1.10\include\actions.inc(89) : error 001: expected token: ";", but found "<newline>"
//
// 3 Errors.


sm 1.10

BHaType
03-02-2022, 04:18
Updated

- Fixed compilation errors for sm 1.10
- Allowed to return NULL for InitialContainedAction
- Added msvc

NightlyRaine
03-23-2022, 23:39
Using nb_move_to_cursor and nb_move_to_position seems to crash when a bot is spawned in when using this amazing extension.

And I for the life of me can not figure out how to use or what this is even supposed to do, Am somewhat new to messing with plugins at times. Any help on what it is supposed to do and how to use would be amazing!

/* Used to iterate through all entity actions */
public static native void Actions( int entity, ActionsIteratorCallback callback );

And GetName() doesn't seem to work when getting the name of a result action like
"result.action.GetName()"

BHaType
03-24-2022, 03:09
Using nb_move_to_cursor and nb_move_to_position seems to crash when a bot is spawned in when using this amazing extension.
Thanks for report. Fixed

And I for the life of me can not figure out how to use or what this is even supposed to do, Am somewhat new to messing with plugins at times. Any help on what it is supposed to do and how to use would be amazing!
It will give every action that passed entity contains. There is no way to legally create ArrayList via extension so that's the only way how can you pass multiple objects without creating your own handle type.
#pragma semicolon 1
#pragma newdecls required

#include <sdktools>
#include <actions>

public void OnPluginStart()
{
RegConsoleCmd("sm_actions", sm_actions);
}

public Action sm_actions( int client, int args )
{
int target = GetClientAimTarget(client, false);

if ( target == -1 )
return Plugin_Handled;

BehaviorAction.Actions(target, ActionsIterator);
return Plugin_Handled;
}

public void ActionsIterator( BehaviorAction action )
{
char name[ACTION_NAME_LENGTH];
action.GetName(name, sizeof name);

PrintToChatAll("%s", name);
}
And GetName() doesn't seem to work when getting the name of a result action like

Just tested and seems to be working fine. Make sure you are not trying to access null action or show code so i can replicate issue.

BHaType
03-24-2022, 03:33
Updated

- Fixed crash for nb_move_to_cursor and nb_move_to_position cmds (Thanks to NightlyRaine)
- Added new example

NightlyRaine
03-24-2022, 17:49
This is how I was trying to use GetName(), if I change result.action.GetName(name, sizeof name); to action.GetName(name, sizeof name); it works, but I can't get the result name.

#pragma semicolon 1
#pragma newdecls required

#include <sourcemod>
#include <sdktools>

#include <actions>

public void OnActionCreated( BehaviorAction action, int actor, const char[] name )
{
if ( strcmp(name, "SurvivorLegsStayClose") == 0 )
{
action.OnUpdatePost = SurvivorLegsStayClose_OnUpdatePost;
}
}

/* ----------------------------------------------------------------------------------------------------*/
/* SurvivorLegsStayClose */
/* ----------------------------------------------------------------------------------------------------*/
public Action SurvivorLegsStayClose_OnUpdatePost( BehaviorAction action, float interval, ActionResult result )
{
if ( result.IsRequestingChange() )
{
if ( result.action != INVALID_ACTION )
{
char name[ACTION_NAME_LENGTH];
result.action.GetName(name, sizeof name);
PrintToServer( "%s", name );
}
}

return Plugin_Continue;
}

In game Console


L 03/24/2022 - 16:36:31: [SM] Exception reported: Invalid action passed (66C1B5F0)
L 03/24/2022 - 16:36:31: [SM] Blaming: GetNameExample.smx
L 03/24/2022 - 16:36:31: [SM] Call stack trace:
L 03/24/2022 - 16:36:31: [SM] [0] BehaviorAction.GetName
L 03/24/2022 - 16:36:31: [SM] [1] Line 27, F:\SteamLibrary\steamapps\common\Left 4 Dead 2\left4dead2\addons\sourcemod\scripting\GetNa meExample.sp::SurvivorLegsStayClose_OnUpdateP ost
133.27: Rochelle:: SurvivorLegsStayClose caused SurvivorLegsStayClose to SUSPEND_FOR SurvivorLegsRegroup (I'm too far away from the closest human survivor)
133.27: Rochelle:: SUSPENDING SurvivorLegsStayClose
133.27: Rochelle:: SUSPENDING SurvivorLegsMoveOn
133.27: Rochelle:: STARTING SurvivorLegsRegroup
SurvivorLegsRegroup<<SurvivorLegsStayClose( SurvivorLegsMoveOn )


And side question, would it be possible to change actions to other existing exactions?
Like when SurvivorLegsMoveOn Suspends_For SurvivorLegsBattleStations, would it be possible to get it to suspend for L4D1SurvivorLegsBattlesStations? Or SurvivorLegsWait? Asking so I know what all is possible.

BHaType
03-26-2022, 01:21
I can't get the result name.

Thanks for report. Fixed.

And side question, would it be possible to change actions to other existing exactions?
Like when SurvivorLegsMoveOn Suspends_For SurvivorLegsBattleStations, would it be possible to get it to suspend for L4D1SurvivorLegsBattlesStations? Or SurvivorLegsWait? Asking so I know what all is possible.

Yep, but you need to create SDKCall for SurvivorLegsBattleStations constructor


#pragma semicolon 1
#pragma newdecls required

#include <sourcemod>
#include <sdktools>

#include <actions>

public void OnActionCreated( BehaviorAction action, int actor, const char[] name )
{
if ( strcmp(name, "SurvivorLegsMoveOn") == 0 )
{
action.OnUpdatePost = SurvivorLegsStayClose_OnUpdatePost;
}
}

/* ----------------------------------------------------------------------------------------------------*/
/* SurvivorLegsStayClose */
/* ----------------------------------------------------------------------------------------------------*/
public Action SurvivorLegsStayClose_OnUpdatePost( BehaviorAction action, float interval, ActionResult result )
{
if ( result.type == SUSPEND_FOR && result.action != INVALID_ACTION )
{
char name[ACTION_NAME_LENGTH];
result.action.GetName(name, sizeof name);

if ( strcmp(name, "SurvivorLegsBattleStations") != 0 )
return Plugin_Continue;

BehaviorAction.Deallocate(result.action);

result.action = L4D1BattleStationAction();
result.type = SUSPEND_FOR;
return Plugin_Changed;
}

return Plugin_Continue;
}

BehaviorAction L4D1BattleStationAction()
{
static Handle call;

if ( !call )
{
StartPrepSDKCall(SDKCall_Raw);
PrepSDKCall_SetSignature(...);
call = EndPrepSDKCall();
}

BehaviorAction action = BehaviorAction.Allocate(0x483C);
SDKCall(call, action);
return action;
}

BHaType
03-26-2022, 01:23
Updated

- Added IsSuspended native
- Added IsStarted native
- Fixed "Invalid action" error for runtime actions (Thanks to NightlyRaine)

Marttt
03-27-2022, 23:28
Just a cool note, if someone wants debug to see some action stuff, you can use "nb_debug BEHAVIOR" in console (in a listen server) to watch.

ThatKidWhoGames
03-29-2022, 14:29
This is how I was trying to use GetName(), if I change result.action.GetName(name, sizeof name); to action.GetName(name, sizeof name); it works, but I can't get the result name.

#pragma semicolon 1
#pragma newdecls required

#include <sourcemod>
#include <sdktools>

#include <actions>

public void OnActionCreated( BehaviorAction action, int actor, const char[] name )
{
if ( strcmp(name, "SurvivorLegsStayClose") == 0 )
{
action.OnUpdatePost = SurvivorLegsStayClose_OnUpdatePost;
}
}

/* ----------------------------------------------------------------------------------------------------*/
/* SurvivorLegsStayClose */
/* ----------------------------------------------------------------------------------------------------*/
public Action SurvivorLegsStayClose_OnUpdatePost( BehaviorAction action, float interval, ActionResult result )
{
if ( result.IsRequestingChange() )
{
if ( result.action != INVALID_ACTION )
{
char name[ACTION_NAME_LENGTH];
result.action.GetName(name, sizeof name);
PrintToServer( "%s", name );
}
}

return Plugin_Continue;
}

In game Console


L 03/24/2022 - 16:36:31: [SM] Exception reported: Invalid action passed (66C1B5F0)
L 03/24/2022 - 16:36:31: [SM] Blaming: GetNameExample.smx
L 03/24/2022 - 16:36:31: [SM] Call stack trace:
L 03/24/2022 - 16:36:31: [SM] [0] BehaviorAction.GetName
L 03/24/2022 - 16:36:31: [SM] [1] Line 27, F:\SteamLibrary\steamapps\common\Left 4 Dead 2\left4dead2\addons\sourcemod\scripting\GetNa meExample.sp::SurvivorLegsStayClose_OnUpdateP ost
133.27: Rochelle:: SurvivorLegsStayClose caused SurvivorLegsStayClose to SUSPEND_FOR SurvivorLegsRegroup (I'm too far away from the closest human survivor)
133.27: Rochelle:: SUSPENDING SurvivorLegsStayClose
133.27: Rochelle:: SUSPENDING SurvivorLegsMoveOn
133.27: Rochelle:: STARTING SurvivorLegsRegroup
SurvivorLegsRegroup<<SurvivorLegsStayClose( SurvivorLegsMoveOn )


And side question, would it be possible to change actions to other existing exactions?
Like when SurvivorLegsMoveOn Suspends_For SurvivorLegsBattleStations, would it be possible to get it to suspend for L4D1SurvivorLegsBattlesStations? Or SurvivorLegsWait? Asking so I know what all is possible.

Just a suggestion, but when writing code, you should define the include files before adding these lines:

#pragma newdecls required
#pragma semicolon 1

This way, any include files which may be old or do not use semicolons for whatever reason should still allow your plugin to compile without errors.

E.4H
03-29-2022, 23:55
Very cood Extension. If possible, could it support l4d1 please?

objir
03-30-2022, 02:07
Getting a crash when the player is pounced by a SI, if Using this with Advanced Bot AI (https://steamcommunity.com/sharedfiles/filedetails/?id=1968764163). Any help please?

BHaType
03-30-2022, 03:12
Very cood Extension. If possible, could it support l4d1 please?

I have almost made done l4d1 version, will be released when I'll make sure that it works correctly.

Getting a crash when the player is pounced by a SI, if Using this with Advanced Bot AI (https://steamcommunity.com/sharedfiles/filedetails/?id=1968764163). Any help please?

It's doesn't make any sense. I have no idea how this vscript addon can cause crash. Try to test it without 3rd party plugins also you can post accelerator crash id and i will try to find out what caused your crash.

objir
03-30-2022, 04:44
@BHaType Thanks for pointing me to Accelerator.

A log (Tested at the beginning of Dead Center Ch.2.):
Got a presubmit token from server: 06b4ba61cd32830875e139b3001bacbd
Uploaded crash dump: Crash ID: EXQT-QIAX-MQFE

Before posting here, I have done some tests and it appears that the only way to stop the crash is to disable either the ai mod or this extension:
1. Tried to launch the game without -insecure: the crash is gone.
2. Launched the game with -insecure but with no smx plugins (even without the ones that come with Sourcemod and the ones that require this extension, like the Shove Direction Fix): still crashed.
(Will this extension be loaded at all if no plugins that require it are enabled?)
3. Launched the game with -insecure and smx plugins, but without this extension: the crash is gone.
4. Launched the game with -insecure and smx plugins, with this extension but without the ai mod: the crash is gone.

Edit:
a result for EXQT-QIAX-MQFE from crash.limetech.org:
https://i.imgur.com/5blfEuW.png

BHaType
03-30-2022, 06:16
@BHaType Thanks for pointing me to Accelerator.

A log (Tested at the beginning of Dead Center Ch.2.):
Got a presubmit token from server: 06b4ba61cd32830875e139b3001bacbd
Uploaded crash dump: Crash ID: EXQT-QIAX-MQFE

Before posting here, I have done some tests and it appears that the only way to stop the crash is to disable either the ai mod or this extension:
1. Tried to launch the game without -insecure: the crash is gone.
2. Launched the game with -insecure but with no smx plugins (even without the ones that come with Sourcemod and the ones that require this extension, like the Shove Direction Fix): still crashed.
(Will this extension be loaded at all if no plugins that require it are enabled?)
3. Launched the game with -insecure and smx plugins, but without this extension: the crash is gone.
4. Launched the game with -insecure and smx plugins, with this extension but without the ai mod: the crash is gone.

Thanks for accelerator crash report but I played 3 campaigns and survival for 9 minutes and not a single crash happened. Are you sure that you have installed latest version if it's not then can you also attach version that you are using?

Will this extension be loaded at all if no plugins that require it are enabled?

Yep, this is necessary because the extension intercepts actions by their "inheritance" system. If some action is lost then subsequent actions will also be lost.

objir
03-30-2022, 06:49
@BHaType Oh Shit... Completely Missed the new version. No more crashes after updating to the latest version. Sorry, my bad.

The old version I was using is last edited on March 2nd.

Dragokas
04-13-2022, 11:22
Wow! Very very nice!
Thanks for making this !!!

Is it per-action specific value? :



public Action OnFriendAction( BehaviorAction action, float interval, ActionResult result )
{
...
int target = action.Get(0x34) & 0xFFF;
...



Can we know more useful offsets?

PS. Surely, looking forward for l4d1 version.
Nice job!

BHaType
04-15-2022, 19:24
Is it per-action specific value? :
Yep, this is offset to member of action class so another action probably will have some another data rather than entity handle.

Can we know more useful offsets?
There is no way to find them except reverse engineering but there is "pattern" for same actions type. In other words if you find offset to some data then probably similiar actions will store same data for that offset.
For example TankAttack stores tank's target at 0x34 offset which means there is a high chance that other attack actions(SpitterAttack, SmokerAttack etc...) will also store target at 0x34.

BHaType
05-06-2022, 23:07
Updated

- Fully rewritten version (no backward support, sorry)
- Added new natives
- Added some debug commands
- Added Left 4 Dead support

Dragokas
05-07-2022, 06:21
Best!

BHaType
05-07-2022, 09:55
Updated
- Fixed issue where hooks called the wrong handler

HarryPotter
05-10-2022, 06:25
Updated

- Fully rewritten version (no backward support, sorry)
- Added new natives
- Added some debug commands
- Added Left 4 Dead support


nice, glad to see you support l4d1 :)

Voevoda
05-15-2022, 15:33
L 05/15/2022 - 03:15:28: SourceMod error session started
L 05/15/2022 - 03:15:28: Info (map "c5m1_waterfront") (file "/home/l4d2server/serverfiles/left4dead2/addons/sourcemod/logs/errors_20220515.log")
L 05/15/2022 - 03:15:28: [SM] Unable to load extension "actions.ext": /home/l4d2server/serverfiles/left4dead2/addons/sourcemod/extensions/actions.ext.so: cannot open shared object file: No such file or directory
L 05/15/2022 - 03:150: [SM] Unable to load plugin "l4d2_shove_fix.smx": Native "BehaviorAction.OnShoved.set" was not found
L 05/15/2022 - 03:152: Error log file session closed.

BHaType
05-15-2022, 19:02
...

Error is quite clear. Read it

Voevoda
05-15-2022, 19:07
Error is quite clear. Read it

installed everything still error

BHaType
05-15-2022, 19:33
installed everything still error

Atleast provide some info about your server. I have no idea what you did to install it but error clearly says that it can't find extension.

Just so you know installation steps:
1. Download archive
2. Put extension and autoload file to sourcemod/extension folder (Make sure you placed right extension since they all depends by game and OS)
3. Put gamedata file to sourcemod/gamedata folder
4. Restart server
5. Done

BHaType
05-16-2022, 02:22
Updated


- Change std::vector to std::deque in actions_manager
- Fix runtime action not working properly
- Fix passing CBaseEntity args
- Add runtime action check to deallocate native
- Fixed post hooks

Voevoda
05-16-2022, 05:37
Atleast provide some info about your server. I have no idea what you did to install it but error clearly says that it can't find extension.

Just so you know installation steps:
1. Download archive
2. Put extension and autoload file to sourcemod/extension folder (Make sure you placed right extension since they all depends by game and OS)
3. Put gamedata file to sourcemod/gamedata folder
4. Restart server
5. Done


L 05/16/2022 - 12:36:01: SourceMod error session started
L 05/16/2022 - 12:36:01: Info (map "c5m1_waterfront") (file "/home/l4d2server/serverfiles/left4dead2/addons/sourcemod/logs/errors_20220516.log")
L 05/16/2022 - 12:36:01: [SM] Unable to load extension "actions.ext": /home/l4d2server/serverfiles/left4dead2/addons/sourcemod/extensions/actions.ext.2.l4d2.so: undefined symbol: _ZNSt7__cxx1112basic_stringIcSt11char_traitsI cESaIcEE9_M_createERjj
L 05/16/2022 - 12:36:04: [SM] Unable to load plugin "l4d2_shove_fix.smx": Native "BehaviorAction.OnShoved.set" was not found



[01] Stripper (1.2.2) by BAILOPAN
[02] L4DToolZ (1.0.0.9r1) by Ivailosp
[03] SourceMod (1.10.0.6538) by AlliedModders LLC
[04] DHooks (2.2.0-detours17) by Dr!fter and Peace-Maker
[05] SDK Tools (1.10.0.6538) by AlliedModders LLC
[06] SDK Hooks (1.10.0.6538) by AlliedModders LLC
[07] VoiceHook (3.0) by Accelerator
[08] SteamWorks Extension (1.2.3) by Kyle Sanderson

BHaType
05-16-2022, 07:03
/home/l4d2server/serverfiles/left4dead2/addons/sourcemod/extensions/actions.ext.2.l4d2.so: undefined symbol: _ZNSt7__cxx1112basic_stringIcSt11char_traitsI cESaIcEE9_M_createERjj


Just tested with linux server and extension loaded without any errors. I have no idea how to fix it.
It was issue with latest compilers

BHaType
05-18-2022, 01:34
Updated

- Fixed link issue for latest compilers

BHaType
05-20-2022, 02:46
Updated

- CMake reconfigure
- Add pending actions
- Add handlers result safety check
- Fix actor for OnActionCreated callback
- Fix custom actions

Psyk0tik
05-27-2022, 03:17
I would like to request adding information to the include file to make the whole library optional. This will allow plugins to make the extension an optional dependency.

Something like this:
public Extension __ext_actions =
{
name = "Actions",
file = "actions.ext",
autoload = 1,
#if defined REQUIRE_EXTENSIONS
required = 1,
#else
required = 0,
#endif
};

#if !defined REQUIRE_EXTENSIONS
public void __ext_actions_SetNTVOptional()
{
MarkNativeAsOptional("ActionResult.GetReason");
MarkNativeAsOptional("ActionResult.SetReason");
MarkNativeAsOptional("ActionResult.type.get");
MarkNativeAsOptional("ActionResult.type.set");
MarkNativeAsOptional("ActionResult.action.get");
MarkNativeAsOptional("ActionResult.action.set");
MarkNativeAsOptional("ActionResult.IsRequestingChange");
MarkNativeAsOptional("ActionDesiredResult.priority.get");
MarkNativeAsOptional("ActionDesiredResult.priority.set");
MarkNativeAsOptional("ActionsManager.Create");
MarkNativeAsOptional("ActionsManager.Allocate");
MarkNativeAsOptional("ActionsManager.Deallocate");
MarkNativeAsOptional("ActionsManager.Iterator");
MarkNativeAsOptional("ActionsManager.GetAction");
MarkNativeAsOptional("BehaviorAction.BehaviorAction");
MarkNativeAsOptional("BehaviorAction.StorePendingEventResult");
MarkNativeAsOptional("BehaviorAction.GetName");
MarkNativeAsOptional("BehaviorAction.Get");
MarkNativeAsOptional("BehaviorAction.Set");
MarkNativeAsOptional("BehaviorAction.Parent.get");
MarkNativeAsOptional("BehaviorAction.Child.get");
MarkNativeAsOptional("BehaviorAction.Under.get");
MarkNativeAsOptional("BehaviorAction.Above.get");
MarkNativeAsOptional("BehaviorAction.Actor.get");
MarkNativeAsOptional("BehaviorAction.IsSuspended.get");
MarkNativeAsOptional("BehaviorAction.IsSuspended.set");
MarkNativeAsOptional("BehaviorAction.IsStarted.get");
MarkNativeAsOptional("BehaviorAction.IsStarted.set");
MarkNativeAsOptional("BehaviorAction.OnStart.set");
MarkNativeAsOptional("BehaviorAction.OnUpdate.set");
MarkNativeAsOptional("BehaviorAction.OnEnd.set");
MarkNativeAsOptional("BehaviorAction.OnSuspend.set");
MarkNativeAsOptional("BehaviorAction.OnResume.set");
MarkNativeAsOptional("BehaviorAction.OnInitialContainedAction.set");
MarkNativeAsOptional("BehaviorAction.OnLeaveGround.set");
MarkNativeAsOptional("BehaviorAction.OnLandOnGround.set");
MarkNativeAsOptional("BehaviorAction.OnContact.set");
MarkNativeAsOptional("BehaviorAction.OnMoveToSuccess.set");
MarkNativeAsOptional("BehaviorAction.OnMoveToFailure.set");
MarkNativeAsOptional("BehaviorAction.OnStuck.set");
MarkNativeAsOptional("BehaviorAction.OnUnStuck.set");
MarkNativeAsOptional("BehaviorAction.OnPostureChanged.set");
MarkNativeAsOptional("BehaviorAction.OnAnimationActivityComplete.se t");
MarkNativeAsOptional("BehaviorAction.OnAnimationActivityInterrupted .set");
MarkNativeAsOptional("BehaviorAction.OnAnimationEvent.set");
MarkNativeAsOptional("BehaviorAction.OnIgnite.set");
MarkNativeAsOptional("BehaviorAction.OnInjured.set");
MarkNativeAsOptional("BehaviorAction.OnKilled.set");
MarkNativeAsOptional("BehaviorAction.OnOtherKilled.set");
MarkNativeAsOptional("BehaviorAction.OnSight.set");
MarkNativeAsOptional("BehaviorAction.OnLostSight.set");
MarkNativeAsOptional("BehaviorAction.OnThreatChanged.set");
MarkNativeAsOptional("BehaviorAction.OnSound.set");
MarkNativeAsOptional("BehaviorAction.OnSpokeConcept.set");
MarkNativeAsOptional("BehaviorAction.OnNavAreaChanged.set");
MarkNativeAsOptional("BehaviorAction.OnModelChanged.set");
MarkNativeAsOptional("BehaviorAction.OnPickUp.set");
MarkNativeAsOptional("BehaviorAction.OnShoved.set");
MarkNativeAsOptional("BehaviorAction.OnBlinded.set");
MarkNativeAsOptional("BehaviorAction.OnEnteredSpit.set");
MarkNativeAsOptional("BehaviorAction.OnHitByVomitJar.set");
MarkNativeAsOptional("BehaviorAction.OnCommandAttack.set");
MarkNativeAsOptional("BehaviorAction.OnCommandAssault.set");
MarkNativeAsOptional("BehaviorAction.OnCommandApproachV.set");
MarkNativeAsOptional("BehaviorAction.OnCommandApproachE.set");
MarkNativeAsOptional("BehaviorAction.OnCommandRetreat.set");
MarkNativeAsOptional("BehaviorAction.OnCommandPause.set");
MarkNativeAsOptional("BehaviorAction.OnCommandResume.set");
MarkNativeAsOptional("BehaviorAction.OnCommandString.set");
MarkNativeAsOptional("BehaviorAction.IsAbleToBlockMovementOf.set");
MarkNativeAsOptional("BehaviorAction.OnStartPost.set");
MarkNativeAsOptional("BehaviorAction.OnUpdatePost.set");
MarkNativeAsOptional("BehaviorAction.OnEndPost.set");
MarkNativeAsOptional("BehaviorAction.OnSuspendPost.set");
MarkNativeAsOptional("BehaviorAction.OnResumePost.set");
MarkNativeAsOptional("BehaviorAction.OnInitialContainedActionPost.s et");
MarkNativeAsOptional("BehaviorAction.OnLeaveGroundPost.set");
MarkNativeAsOptional("BehaviorAction.OnLandOnGroundPost.set");
MarkNativeAsOptional("BehaviorAction.OnContactPost.set");
MarkNativeAsOptional("BehaviorAction.OnMoveToSuccessPost.set");
MarkNativeAsOptional("BehaviorAction.OnMoveToFailurePost.set");
MarkNativeAsOptional("BehaviorAction.OnStuckPost.set");
MarkNativeAsOptional("BehaviorAction.OnUnStuckPost.set");
MarkNativeAsOptional("BehaviorAction.OnPostureChangedPost.set");
MarkNativeAsOptional("BehaviorAction.OnAnimationActivityCompletePos t.set");
MarkNativeAsOptional("BehaviorAction.OnAnimationActivityInterrupted Post.set");
MarkNativeAsOptional("BehaviorAction.OnAnimationEventPost.set");
MarkNativeAsOptional("BehaviorAction.OnIgnitePost.set");
MarkNativeAsOptional("BehaviorAction.OnInjuredPost.set");
MarkNativeAsOptional("BehaviorAction.OnKilledPost.set");
MarkNativeAsOptional("BehaviorAction.OnOtherKilledPost.set");
MarkNativeAsOptional("BehaviorAction.OnSightPost.set");
MarkNativeAsOptional("BehaviorAction.OnLostSightPost.set");
MarkNativeAsOptional("BehaviorAction.OnThreatChangedPost.set");
MarkNativeAsOptional("BehaviorAction.OnSoundPost.set");
MarkNativeAsOptional("BehaviorAction.OnSpokeConceptPost.set");
MarkNativeAsOptional("BehaviorAction.OnNavAreaChangedPost.set");
MarkNativeAsOptional("BehaviorAction.OnModelChangedPost.set");
MarkNativeAsOptional("BehaviorAction.OnPickUpPost.set");
MarkNativeAsOptional("BehaviorAction.OnShovedPost.set");
MarkNativeAsOptional("BehaviorAction.OnBlindedPost.set");
MarkNativeAsOptional("BehaviorAction.OnEnteredSpitPost.set");
MarkNativeAsOptional("BehaviorAction.OnHitByVomitJarPost.set");
MarkNativeAsOptional("BehaviorAction.OnCommandAttackPost.set");
MarkNativeAsOptional("BehaviorAction.OnCommandAssaultPost.set");
MarkNativeAsOptional("BehaviorAction.OnCommandApproachVPost.set");
MarkNativeAsOptional("BehaviorAction.OnCommandApproachEPost.set");
MarkNativeAsOptional("BehaviorAction.OnCommandRetreatPost.set");
MarkNativeAsOptional("BehaviorAction.OnCommandPausePost.set");
MarkNativeAsOptional("BehaviorAction.OnCommandResumePost.set");
MarkNativeAsOptional("BehaviorAction.IsAbleToBlockMovementOfPost.se t");
}
#endif

Also, I think the thread title should be renamed with the prefix "[L4D & L4D2]" or something similar since you added support for L4D1.

Also, also, is there a consistent/reliable way to get the target of an action? In your example callback for "OnShovedPost" you have a "shover" param but I don't know where you got that. Then, in your block example, you use an offset to retrieve the target for the "OnFriendAction" callback, and I am wondering if that offset is always the same for every action or not.

Also, also, also, what are the different function prototypes for the callback functions? In your examples, you use different prototypes so I don't know which one to use for "TankAttack". Maybe a "typedef" or "typeset" in the include will help.
OnStart( BehaviorAction action, int actor, BehaviorAction priorAction, ActionResult result )
OnMyActionStart( BehaviorAction action, int actor, BehaviorAction priorAction, ActionResult result )
OnMyActionUpdate( BehaviorAction action, int actor, float interval, ActionResult result )
OnShovedPost( BehaviorAction action, int actor, int shover, ActionDesiredResult result )
OnSelfAction( BehaviorAction action, int actor, BehaviorAction priorAction, ActionResult result )
OnFriendAction( BehaviorAction action, int actor, BehaviorAction priorAction, ActionResult result )

For my use case, I am experimenting with setting a Tank's target when his current behavior is "TankAttack" so my plugin can decide which survivors to focus.

BHaType
05-27-2022, 06:50
Also, also, is there a consistent/reliable way to get the target of an action? In your example callback for "OnShovedPost" you have a "shover" param but I don't know where you got that. Then, in your block example, you use an offset to retrieve the target for the "OnFriendAction" callback, and I am wondering if that offset is always the same for every action or not.

For my use case, I am experimenting with setting a Tank's target when his current behavior is "TankAttack" so my plugin can decide which survivors to focus.

In most cases offsets will be same for every action but it's still better to check it yourself if you not sure. I have already mentioned some pattern info about offsets (https://forums.alliedmods.net/showpost.php?p=2776939&postcount=23).

Every special infected chooses his target by calling ChooseVictim in ...Attack::Update action handler and return of ChooseVictim is always stored by 0x34 offset. Actually getting current tank target via action is more reliable and proper way since game directly uses this var to make path and it also considers OnContact event handler which makes tank attack who touched him.

About event handlers params. Their params are always same for every action but event handlers in plugins have some differences/additions. First param is always action, then arguments of the default handler, the last is always ActionResult/ActionDesiredResult. I will add typeset soon.

For example default OnShoved handler looks like this
OnShoved(Infected* <- actor, CBaseEntity* <- shover)
so in plugin OnShoved callback will look like this

/* First param is always action, then arguments of the default handler, the last is always ActionResult/ActionDesiredResult */
OnShoved(BehaviorAction action, int actor, int shover, ActionDesiredResult result)

ActionResult is used only for OnStart, Update, OnEnd, OnSuspend, OnResume others use ActionDesiredResult (reference (https://github.com/perilouswithadollarsign/cstrike15_src/blob/f82112a2388b841d72cb62ca48ab1846dfcc11c8/game/server/NextBot/NextBotBehavior.h#L532))


44 43 Action<Infected>::OnStart(Infected*, Action<Infected>*)
45 44 InfectedExecAction::Update(Infected*, float)
46 45 Action<Infected>::OnEnd(Infected*, Action<Infected>*)
47 46 Action<Infected>::OnSuspend(Infected*, Action<Infected>*)
48 47 Action<Infected>::OnResume(Infected*, Action<Infected>*)
49 48 InfectedExecAction::InitialContainedAction(In fected*)
50 49 Action<Infected>::OnLeaveGround(Infected*, CBaseEntity*)
51 50 Action<Infected>::OnLandOnGround(Infected*, CBaseEntity*)
52 51 Action<Infected>::OnContact(Infected*, CBaseEntity*, CGameTrace*)
53 52 Action<Infected>::OnMoveToSuccess(Infected*, Path const*)
54 53 Action<Infected>::OnMoveToFailure(Infected*, Path const*, MoveToFailureType)
55 54 Action<Infected>::OnStuck(Infected*)
56 55 Action<Infected>::OnUnStuck(Infected*)
57 56 Action<Infected>::OnPostureChanged(Infected*)
58 57 Action<Infected>::OnAnimationActivityComplete(Infected*, int)
59 58 Action<Infected>::OnAnimationActivityInterrupted(Infected*, int)
L W Function
60 59 Action<Infected>::OnAnimationEvent(Infected*, animevent_t*)
61 60 Action<Infected>::OnIgnite(Infected*)
62 61 InfectedExecAction::OnInjured(Infected*, CTakeDamageInfo const&)
63 62 Action<Infected>::OnKilled(Infected*, CTakeDamageInfo const&)
64 63 Action<Infected>::OnOtherKilled(Infected*, CBaseCombatCharacter*, CTakeDamageInfo const&)
65 64 Action<Infected>::OnSight(Infected*, CBaseEntity*)
66 65 Action<Infected>::OnLostSight(Infected*, CBaseEntity*)
67 66 Action<Infected>::OnThreatChanged(Infected*, CBaseEntity*)
68 67 Action<Infected>::OnSound(Infected*, CBaseEntity*, Vector const&, KeyValues*)
69 68 Action<Infected>::OnSpokeConcept(Infected*, CBaseCombatCharacter*, CAI_Concept, AI_Response*)
70 69 Action<Infected>::OnNavAreaChanged(Infected*, CNavArea*, CNavArea*)
71 70 Action<Infected>::OnModelChanged(Infected*)
72 71 Action<Infected>::OnPickUp(Infected*, CBaseEntity*, CBaseCombatCharacter*)
73 72 Action<Infected>::OnDrop(Infected*, CBaseEntity*)
74 73 Action<Infected>::OnShoved(Infected*, CBaseEntity*)
75 74 Action<Infected>::OnBlinded(Infected*, CBaseEntity*)
76 75 Action<Infected>::OnEnteredSpit(Infected*)
77 76 Action<Infected>::OnHitByVomitJar(Infected*, CBaseEntity*)
78 77 Action<Infected>::OnCommandAttack(Infected*, CBaseEntity*)
79 78 Action<Infected>::OnCommandAssault(Infected*)
L W Function
80 80 Action<Infected>::OnCommandApproach(Infected*, Vector const&, float)
81 79 Action<Infected>::OnCommandApproach(Infected*, CBaseEntity*)
82 81 Action<Infected>::OnCommandRetreat(Infected*, CBaseEntity*, float)
83 82 Action<Infected>::OnCommandPause(Infected*, float)
84 83 Action<Infected>::OnCommandResume(Infected*)
85 84 Action<Infected>::OnCommandString(Infected*, char const*)
86 85 Action<Infected>::IsAbleToBlockMovementOf(INextBot const*) const



/* Probably missed something */
typeset ActionHandler
{
function Action OnStart(BehaviorAction action, int actor, BehaviorAction action, ActionResult result);
function Action Update(BehaviorAction action, int actor, float, ActionResult result);
function void OnEnd(BehaviorAction action, int actor, BehaviorAction action, ActionResult result);
function Action OnSuspend(BehaviorAction action, int actor, BehaviorAction action, ActionResult result);
function Action OnResume(BehaviorAction action, int actor, BehaviorAction action, ActionResult result);
function Action InitialContainedAction(BehaviorAction action, int actor, BehaviorAction& action);
function Action OnLeaveGround(BehaviorAction action, int actor, int entity, ActionDesiredResult result);
function Action OnLandOnGround(BehaviorAction action, int actor, int entity, ActionDesiredResult result);
function Action OnContact(BehaviorAction action, int actor, int entity, Address trace, ActionDesiredResult result);
function Action OnMoveToSuccess(BehaviorAction action, int actor, Address path, ActionDesiredResult result);
function Action OnMoveToFailure(BehaviorAction action, int actor, Address path, MoveToFailureType type, ActionDesiredResult result);
function Action OnStuck(BehaviorAction action, int actor, ActionDesiredResult result);
function Action OnUnStuck(BehaviorAction action, int actor, ActionDesiredResult result);
function Action OnPostureChanged(BehaviorAction action, int actor, ActionDesiredResult result);
function Action OnAnimationActivityComplete(BehaviorAction action, int actor, int activity, ActionDesiredResult result);
function Action OnAnimationActivityInterrupted(BehaviorAction action, int actor, int activity, ActionDesiredResult result);
function Action OnAnimationEvent(BehaviorAction action, int actor, Address animevent, ActionDesiredResult result);
function Action OnIgnite(BehaviorAction action, int actor, ActionDesiredResult result);
function Action OnInjured(BehaviorAction action, int actor, Address takedamageinfo, ActionDesiredResult result);
function Action OnKilled(BehaviorAction action, int actor, Address takedamageinfo, ActionDesiredResult result);
function Action OnOtherKilled(BehaviorAction action, int actor, int other, Address takedamageinfo, ActionDesiredResult result);
function Action OnSight(BehaviorAction action, int actor, int subject, ActionDesiredResult result);
function Action OnLostSight(BehaviorAction action, int actor, int subject, ActionDesiredResult result);
function Action OnThreatChanged(BehaviorAction action, int actor, int threat, ActionDesiredResult result);
function Action OnSound(BehaviorAction action, int actor, int entity, float vec[3], Address keyvalues, ActionDesiredResult result);
function Action OnSpokeConcept(BehaviorAction action, int actor, int who, Address concept, Address response, Address unknown, ActionDesiredResult result);
function Action OnNavAreaChanged(BehaviorAction action, int actor, Address newArea, Address oldArea, ActionDesiredResult result);
function Action OnModelChanged(BehaviorAction action, int actor, ActionDesiredResult result);
function Action OnPickUp(BehaviorAction action, int actor, int entity, int giver, ActionDesiredResult result);
function Action OnDrop(BehaviorAction action, int actor, int item, ActionDesiredResult result);
function Action OnShoved(BehaviorAction action, int actor, int shover, ActionDesiredResult result);
function Action OnBlinded(BehaviorAction action, int actor, int blinder, ActionDesiredResult result);
function Action OnEnteredSpit(BehaviorAction action, int actor, ActionDesiredResult result);
function Action OnHitByVomitJar(BehaviorAction action, int actor, int who, ActionDesiredResult result);
function Action OnCommandAttack(BehaviorAction action, int actor, int victim, ActionDesiredResult result);
function Action OnCommandAssault(BehaviorAction action, int actor, ActionDesiredResult result);
function Action OnCommandApproach(BehaviorAction action, int actor, const float pos[3], float range, ActionDesiredResult result);
function Action OnCommandApproach(BehaviorAction action, int actor, int goal, ActionDesiredResult result);
function Action OnCommandRetreat(BehaviorAction action, int actor, int threat, float range, ActionDesiredResult result);
function Action OnCommandPause(BehaviorAction action, int actor, float duration, ActionDesiredResult result);
function Action OnCommandResume(BehaviorAction action, int actor, ActionDesiredResult result);
function Action OnCommandString(BehaviorAction action, int actor, const char[] command, ActionDesiredResult result);
function Action IsAbleToBlockMovementOf(BehaviorAction action, int actor, Address nextbot, ActionDesiredResult result);
}

BHaType
05-27-2022, 06:52
Updated

- Docs reconfigure
- Fixed crash for multiple hooks on same event handler and default action result
- Renamed thread
- Added extension optionality (Thanks to Psyk0tik)


Edit:
Updated

- Added typeset to include

Psyk0tik
05-27-2022, 07:23
...

Alright, thank you for all the information.

(Also thank you for the latest update!)

HarryPotter
05-30-2022, 06:50
sm 1.10 compile error


include\actions.inc(319) : error 092: number of arguments does not match definition



319: StoreToAddress(view_as<Address>(this) + view_as<Address>(offset), data, type, updateMemAccess);

BHaType
05-30-2022, 21:38
include\actions.inc(319) : error 092: number of arguments does not match definition


Thanks, fixed.

Updated

- Fixed compilation error for SourceMod version < 11.0

jackz
07-27-2022, 00:23
I'm trying to convert the witch force attack command you had made for your old l4d2_behavior plugin to this, but I'm not seeing an easy way to do it, as I want to do it on demand and not based on an event.

I got it kind of working but depending on the witch's current action it would not find the action (ActionsManager.GetAction returns INVALID_ACTION), or it would work but then the witch would be stuck on WitchExecAction forever and is unkillable.

In addition, the plugin had gamedata for WitchAttack but behavior doesn't, would I still need that?

Code: https://gist.github.com/Jackzmc/c12b169610fd4a917bb28b127005d6b9

BHaType
07-27-2022, 02:25
I'm trying to convert the witch force attack command you had made for your old l4d2_behavior plugin to this, but I'm not seeing an easy way to do it, as I want to do it on demand and not based on an event.

I got it kind of working but depending on the witch's current action it would not find the action (ActionsManager.GetAction returns INVALID_ACTION), or it would work but then the witch would be stuck on WitchExecAction forever and is unkillable.

Code: https://gist.github.com/Jackzmc/c12b169610fd4a917bb28b127005d6b9

Whenever witch spawn her first action is WitchExecAction which creates WitchBehavior which creates WitchIdle/WitchWander (depends on sv_force_time_of_day and witch_force_wander convars) and so on. WitchExecAction and WitchBehavior are her first actions and they are handling her fire and death states you should never change or suspend them until you really want it.

So this is witch actions when she spawned:
WitchExecAction -> WitchBehavior -> WitchIdle/WitchWander/WitchAttack/...

As you can see the best way is to always get next action after WitchBehavior.
You can easily do this by using action child property.

BehaviorAction GetBestSuspendable(int witch)
{
BehaviorAction action = ActionsManager.GetAction(witch, "WitchBehavior");
return action.Child;
}


or you can change only specific actions. Check code below.

#pragma semicolon 1
#pragma newdecls required

#include <sourcemod>
#include <sdktools>

// #include <l4d2_behavior>
#include <actions>

Handle g_hWitchAttack;
int g_iAttackTarget;

public void OnPluginStart()
{
GameData data = new GameData("l4d2_behavior");

StartPrepSDKCall(SDKCall_Raw);
PrepSDKCall_SetFromConf(data, SDKConf_Signature, "WitchAttack::WitchAttack");
PrepSDKCall_AddParameter(SDKType_CBaseEntity, SDKPass_Pointer, VDECODE_FLAG_ALLOWNULL | VDECODE_FLAG_ALLOWWORLD);
g_hWitchAttack = EndPrepSDKCall();

LoadTranslations("common.phrases");

delete data;

RegAdminCmd("sm_witch_attack", Command_WitchAttack, ADMFLAG_CHEATS);
}

public Action Command_WitchAttack(int client, int args) {
if(args < 1) {
ReplyToCommand(client, "Usage: sm_witch_attack <user> [# of witches or 0 for all]");
}else{
char arg1[32];
GetCmdArg(1, arg1, sizeof(arg1));
char target_name[MAX_TARGET_LENGTH];
int target_list[1], target_count;
bool tn_is_ml;
if ((target_count = ProcessTargetString(
arg1,
client,
target_list,
1,
COMMAND_FILTER_ALIVE, /* Only allow alive players */
target_name,
sizeof(target_name),
tn_is_ml)) <= 0)
{
/* This function replies to the admin with a failure message */
ReplyToTargetError(client, target_count);
return Plugin_Handled;
}

GetCmdArg(2, arg1, sizeof(arg1));
int maxCount = StringToInt(arg1);
if(maxCount < 0) maxCount = 0;
int count;
int target = target_list[0];

if(GetClientTeam(target) == 2) {
g_iAttackTarget = target;
int witch = INVALID_ENT_REFERENCE;
while ((witch = FindEntityByClassname(witch, "witch")) != INVALID_ENT_REFERENCE)
{
BehaviorAction action = GetBestSuspendable(witch);

if( action == INVALID_ACTION )
{
/* What the hell is she doing (staggered?) */
return Plugin_Stop;
}

/* We have found best suspendable */
/* Hook update function */
action.OnUpdate = OnActionUpdate;

if(maxCount > 0 && ++count >= maxCount) break;
}
ShowActivity(client, "%d witches target %s", count, target_name);
}else{
ReplyToTargetError(client, target_count);
}
}
return Plugin_Handled;
}

public Action OnActionUpdate(BehaviorAction action, int actor, float interval, ActionResult result)
{
/* Change to witch attack */
result.type = CHANGE_TO;
result.action = CreateWitchAttackAction(g_iAttackTarget);
result.SetReason("Admin demand");
return Plugin_Handled;
}

BehaviorAction GetBestSuspendable(int witch)
{
/* We check for her idle, wander, angry, attack and retreat actions */
BehaviorAction action = ActionsManager.GetAction(witch, "WitchIdle");

if (action == INVALID_ACTION)
{
action = ActionsManager.GetAction(witch, "WitchWander");

if (action == INVALID_ACTION)
{
action = ActionsManager.GetAction(witch, "WitchAngry");

if (action == INVALID_ACTION)
{
action = ActionsManager.GetAction(witch, "WitchAttack");

if (action == INVALID_ACTION)
{
return ActionsManager.GetAction(witch, "WitchRetreat");
}
}
}
}

return action;
}

BehaviorAction CreateWitchAttackAction(int target = 0)
{
BehaviorAction action = ActionsManager.Allocate(18556);
SDKCall(g_hWitchAttack, action, target);
return action;
}


One more note about StorePendingEventResult. I highly recommend to don't use it until next version because for now it keeps default purpose and stores new result without propagating to other actions this can lead to the loss of actions.


In addition, the plugin had gamedata for WitchAttack but behavior doesn't, would I still need that?


You need gamedata otherwise you won't be able to call the WitchAttack constructor.

jackz
07-27-2022, 13:05
Thanks, I was just looking at ext_actions_dump but didn't realize they were a hierarchically.

Works great now, thanks for explaining it.

BHaType
08-24-2022, 20:16
Updated

- Added Team Fortress 2 support
- Added contextual query hooks
- Added natives to effectively change event result
- Added new convars
- Added new commands
- Fixed string type args for event handlers
- Fixed OnEnteredSpit event handler
- Changed StorePendingEventResult to propagate result but it still doesn't respect result priority
- Changed from hardcoded offsets to use gamedata

L4D2Noob
08-25-2022, 02:21
[06] <FAILED> file "actions.ext.2.l4d2.so": /game/left4dead2/addons/sourcemod/extensions/actions.ext.2.l4d2.so: undefined symbol: _ZSt24__throw_out_of_range_fmtPKcz

BHaType
08-25-2022, 04:50
Updated

- Fixed linux link error (Thanks to L4D2Noob for report)

Gazyi
08-25-2022, 15:29
Thanks for extension and TF2 support update.
I've tried to port TF2 version of extension to Insurgency. I guess, their NextBots are based on TF2 version because they have all same functions and offsets as TF2 one, but there's also some INS specific actions. However, server crashes because "OnIntentionReset" function loops and stack overflows.


actions.ext.2.insurgency.dll!OnIntentionReset ()Line 27 C++
actions.ext.2.insurgency.dll!fastdelegate::Fa stDelegate<void>::InvokeStaticFunction()Line 968 C++
actions.ext.2.insurgency.dll!fastdelegate::Fa stDelegate<void>::operator()()Line 928 C++
actions.ext.2.insurgency.dll!__SourceHook_MFH Cls_OnIntentionReset::CMyDelegateImpl::Call() Line 7 C++
actions.ext.2.insurgency.dll!__SourceHook_MFH Cls_OnIntentionReset::Func()Line 7 C++

Any ideas what's cause that? Or maybe you have plans for adding INS and DOI support?

Here's gamedata. The address and signatures are based on what I've seen in TF2 gamedata.
"Games"
{
"#default"
{
"Offsets"
{
"Destructor"
{
"linux" "1"
"windows" "0"
}
}
}

"insurgency"
{
"Offsets"
{
"IIntention::Reset"
{
"linux" "46"
"windows" "45"
}

// IIntention offsets?
"ShouldPickUp"
{
"linux" "2"
"windows" "1"
}
"ShouldHurry"
{
"linux" "3"
"windows" "2"
}
"ShouldRetreat"
{
"linux" "4"
"windows" "3"
}
"ShouldAttack"
{
"linux" "5"
"windows" "4"
}
"IsHindrance"
{
"linux" "6"
"windows" "5"
}
"SelectTargetPoint"
{
"linux" "7"
"windows" "6"
}
"IsPositionAllowed"
{
"linux" "8"
"windows" "7"
}
"SelectMoreDangerousThreat"
{
"linux" "9"
"windows" "8"
}
"ShouldWalk"
{
"linux" "10"
"windows" "9"
}
"ShouldIronsight"
{
"linux" "11"
"windows" "10"
}
"ShouldProne"
{
"linux" "12"
"windows" "11"
}
"ShouldPursue"
{
"linux" "13"
"windows" "12"
}

// MainAction offsets
"OnStart"
{
"linux" "49"
"windows" "48"
}
"Update"
{
"linux" "50"
"windows" "49"
}
"OnEnd"
{
"linux" "51"
"windows" "50"
}
"OnSuspend"
{
"linux" "52"
"windows" "51"
}
"OnResume"
{
"linux" "53"
"windows" "52"
}
"InitialContainedAction"
{
"linux" "54"
"windows" "53"
}
"OnLeaveGround"
{
"linux" "55"
"windows" "54"
}
"OnLandOnGround"
{
"linux" "56"
"windows" "55"
}
"OnContact"
{
"linux" "57"
"windows" "56"
}
"OnMoveToSuccess"
{
"linux" "58"
"windows" "57"
}
"OnMoveToFailure"
{
"linux" "59"
"windows" "58"
}
"OnStuck"
{
"linux" "60"
"windows" "59"
}
"OnUnStuck"
{
"linux" "61"
"windows" "60"
}
"OnPostureChanged"
{
"linux" "62"
"windows" "61"
}
"OnAnimationActivityComplete"
{
"linux" "63"
"windows" "62"
}
"OnAnimationActivityInterrupted"
{
"linux" "64"
"windows" "63"
}
"OnAnimationEvent"
{
"linux" "65"
"windows" "64"
}
"OnIgnite"
{
"linux" "66"
"windows" "65"
}
"OnInjured"
{
"linux" "67"
"windows" "66"
}
"OnKilled"
{
"linux" "68"
"windows" "67"
}
"OnOtherKilled"
{
"linux" "69"
"windows" "68"
}
"OnSight"
{
"linux" "70"
"windows" "69"
}
"OnLostSight"
{
"linux" "71"
"windows" "70"
}
"OnSound"
{
"linux" "72"
"windows" "71"
}
"OnSpokeConcept"
{
"linux" "73"
"windows" "72"
}
"OnWeaponFired"
{
"linux" "74"
"windows" "73"
}
"OnNavAreaChanged"
{
"linux" "75"
"windows" "64"
}
"OnModelChanged"
{
"linux" "76"
"windows" "75"
}
"OnPickUp"
{
"linux" "77"
"windows" "76"
}
"OnDrop"
{
"linux" "78"
"windows" "77"
}
"OnActorEmoted"
{
"linux" "79"
"windows" "78"
}
"OnShoved"
{
"linux" "87"
"windows" "86"
}
"OnBlinded"
{
"linux" "88"
"windows" "87"
}
"OnCommandAttack"
{
"linux" "80"
"windows" "79"
}
"OnCommandApproachVector"
{
"linux" "81"
"windows" "81"
}
"OnCommandApproachEntity"
{
"linux" "82"
"windows" "80"
}
"OnCommandRetreat"
{
"linux" "83"
"windows" "82"
}
"OnCommandPause"
{
"linux" "84"
"windows" "83"
}
"OnCommandResume"
{
"linux" "85"
"windows" "84"
}
"OnCommandString"
{
"linux" "86"
"windows" "85"
}
"IsAbleToBlockMovementOf"
{
"linux" "97"
"windows" "96"
}
"OnTerritoryContested"
{
"linux" "89"
"windows" "88"
}
"OnTerritoryCaptured"
{
"linux" "90"
"windows" "89"
}
"OnTerritoryLost"
{
"linux" "91"
"windows" "90"
}
"OnWin"
{
"linux" "92"
"windows" "91"
}
"OnLose"
{
"linux" "93"
"windows" "92"
}
"OnHeardFootsteps"
{
"linux" "94"
"windows" "93"
}
"OnSeeSomethingSuspicious"
{
"linux" "95"
"windows" "94"
}
"OnOrderReceived"
{
"linux" "96"
"windows" "95"
}
}
"Addresses"
{
"CINSNextBot::CINSNextBotIntention::Reset"
{
"signature" "CINSNextBot::CINSNextBotIntention::Reset"
"linux"
{
"offset" "8"
}
"windows"
{
"read" "4"
}
}
}
"Signatures"
{
"CINSNextBot::CINSNextBotIntention::Reset"
{
// first xref to "const CINSNextBot::CINSNextBotIntention::`vftable'"
// push 50h
// mov dword ptr [esi], offset ??_7CINSNextBotIntention@CINSNextBot@@6B@
// 6A 50 C7 06 F8 F3 54 10 C7 46
"windows" "\x6A\x50\xC7\x06\xF8\xF3\x2A\x2A\xC7\x46"
"linux" "@_ZN11CINSNextBot20CINSNextBotIntentionC2EPS_"
}
}
}
}

BHaType
08-28-2022, 04:42
Thanks for extension and TF2 support update.
I've tried to port TF2 version of extension to Insurgency. I guess, their NextBots are based on TF2 version because they have all same functions and offsets as TF2 one, but there's also some INS specific actions. However, server crashes because "OnIntentionReset" function loops and stack overflows.
Any ideas what's cause that? Or maybe you have plans for adding INS and DOI support?

For now I don't plan to add support for INS or DOI, maybe someday. As for crash, it could be anything. I suggest to debug Intention interface to be sure you are hooking right function.

tRololo312312
09-12-2022, 17:18
I can't get your example of "SurvivorHealSelf" to work. Bots refuse to heal even if B&W.

BHaType
09-12-2022, 18:53
I can't get your example of "SurvivorHealSelf" to work. Bots refuse to heal even if B&W.

Recent update broke this extension on listen servers. I will fix it later.

If you host dedicated server then try example below. Example that provided in first post has bad black and white detection method.
#pragma semicolon 1
#pragma newdecls required

#include <sourcemod>
#include <sdktools>

#include <actions>

ConVar survivor_max_incapacitated_count;

public void OnPluginStart()
{
survivor_max_incapacitated_count = FindConVar("survivor_max_incapacitated_count");
}

public void OnActionCreated( BehaviorAction action, int actor, const char[] name )
{
/* Hooking self healing action (when bot wants to heal self) */
if ( strcmp(name, "SurvivorHealSelf") == 0 )
action.OnStart = OnSelfAction;

/* Hooking friend healing action (when bot wants to heal someone) */
if ( strcmp(name, "SurvivorHealFriend") == 0 )
action.OnStartPost = OnFriendAction;

/* Hooking take pills action (when bot wants to take pills) */
if ( strcmp(name, "SurvivorTakePills") == 0 )
action.OnStart = OnSelfAction;

/* Hooking give pills action (when bot wants to give pills) */
if ( strcmp(name, "SurvivorGivePillsToFriend") == 0 )
action.OnStartPost = OnFriendAction;
}

public Action OnSelfAction( BehaviorAction action, int actor, BehaviorAction priorAction, ActionResult result )
{
/* When bot will be about to start healing/taking pills we chech if he's black & white */
/* if he is then we allow to heal otherwise no */
if (IsGoingToDie(actor))
return Plugin_Continue;

action.Done("I am not black and white");
return Plugin_Handled;
}

public Action OnFriendAction( BehaviorAction action, int actor, BehaviorAction priorAction, ActionResult result )
{
/* When bot will be about to start healing/giving pills to someone, we chech if friend is black & white */
/* if friend is then we allow to give heal otherwise no */
int target = action.Get(0x34) & 0xFFF;

if (!IsGoingToDie(target))
{
action.Done("My friend isn't black and white");
return Plugin_Handled;
}

return Plugin_Continue;
}

bool IsGoingToDie(int client)
{
return GetEntProp(client, Prop_Send, "m_currentReviveCount") >= survivor_max_incapacitated_count.IntValue;
}

tRololo312312
09-22-2022, 14:41
Ok this one works, thanks. I guess it had to do with the return being different.

sonic155
11-06-2022, 21:12
L 11/06/2022 - 19:08:58: [Actions] Failed to find address for "SurvivorIntention::Reset" key. Check your gamedata...
L 11/06/2022 - 19:08:58: [Actions] Failed to find address for "HunterIntention::Reset" key. Check your gamedata...
L 11/06/2022 - 19:08:58: [Actions] Failed to find address for "BoomerIntention::Reset" key. Check your gamedata...
L 11/06/2022 - 19:08:58: [Actions] Failed to find address for "TankIntention::Reset" key. Check your gamedata...
L 11/06/2022 - 19:08:58: [Actions] Failed to find address for "InfectedIntention::Reset" key. Check your gamedata...
L 11/06/2022 - 19:08:58: [Actions] Failed to find address for "WitchIntention::Reset" key. Check your gamedata...
L 11/06/2022 - 19:08:58: [Actions] Failed to find address for "SmokerIntention::Reset" key. Check your gamedata...
L 11/06/2022 - 19:08:58: [Actions] Failed to find address for "ChargerIntention::Reset" key. Check your gamedata...
L 11/06/2022 - 19:08:58: [Actions] Failed to find address for "JockeyIntention::Reset" key. Check your gamedata...
L 11/06/2022 - 19:08:58: [Actions] Failed to find address for "SpitterIntention::Reset" key. Check your gamedata...

BHaType
11-07-2022, 02:06
...

Extension isn't supported on listen servers until steam game and dedicated server contents will match.
In order to fix this I need to make two gamedata's for listen and dedicated servers which would make gamedata more inconvenient to maintain.

So for now this extension works only for dedicated servers

sonic155
11-07-2022, 22:27
ok thanks for letting me know =)

keep up the great work ether way =D

tommirommi
11-18-2022, 22:32
Update now broke it for dedicated aswell.

HarryPotter
11-19-2022, 01:19
Confirm now after recent l4d2 update, errors in dedicated server

L 11/19/2022 - 14:15:17: [Actions] Failed to find address for "SurvivorIntention::Reset" key. Check your gamedata...
L 11/19/2022 - 14:15:17: [Actions] Failed to find address for "HunterIntention::Reset" key. Check your gamedata...
L 11/19/2022 - 14:15:17: [Actions] Failed to find address for "BoomerIntention::Reset" key. Check your gamedata...
L 11/19/2022 - 14:15:17: [Actions] Failed to find address for "TankIntention::Reset" key. Check your gamedata...
L 11/19/2022 - 14:15:17: [Actions] Failed to find address for "InfectedIntention::Reset" key. Check your gamedata...
L 11/19/2022 - 14:15:17: [Actions] Failed to find address for "WitchIntention::Reset" key. Check your gamedata...
L 11/19/2022 - 14:15:17: [Actions] Failed to find address for "SmokerIntention::Reset" key. Check your gamedata...
L 11/19/2022 - 14:15:17: [Actions] Failed to find address for "ChargerIntention::Reset" key. Check your gamedata...
L 11/19/2022 - 14:15:17: [Actions] Failed to find address for "JockeyIntention::Reset" key. Check your gamedata...
L 11/19/2022 - 14:15:17: [Actions] Failed to find address for "SpitterIntention::Reset" key. Check your gamedata...

BHaType
11-19-2022, 18:22
Updated

- Fix for latest game update

HarryPotter
11-20-2022, 06:50
Updated

- Fix for latest game update


No erros so far, thanks

Edison1318
11-22-2022, 07:01
I have a question, is it possible to perform Action like SurvivorLegsRegroup on SurvivorBot to specific survivor rather than hooking action event?

BHaType
11-23-2022, 01:11
I have a question, is it possible to perform Action like SurvivorLegsRegroup on SurvivorBot to specific survivor rather than hooking action event?

It's possible but it really depends on what you want to achieve.

sm_approach_crosshair - Makes every survivor bot go to crosshair position
sm_approach_me - Makes every survivor bot go to approach you
sm_regroup_to_target - Makes every bot to regroup to your crosshair target (target could be literally any entity)

Edison1318
11-23-2022, 02:14
It's possible but it really depends on what you want to achieve.

sm_approach_crosshair - Makes every survivor bot go to crosshair position
sm_approach_me - Makes every survivor bot go to approach you
sm_regroup_to_target - Makes every bot to regroup to your crosshair target (target could be literally any entity)

Ah I see, looks complicated in the code but thanks :)

Edited: Yeah, to perform a specific function for SurvivorBots looks like I need to get the signatures for that

Red Flame
01-17-2023, 09:55
This extension makes it impossible to use quit, exit or _restart cmds.
Server just not responding after using these commands unless you handle it manually.
OS: Ubuntu 22.04.1
Game: L4D2
Sourcemod version: 1.11.0.6927
Metamod version: 1.11.0-dev+1148

Update:
Tested it on clean windows and linux servers
Windows: had no problems with that;
Linux: the same problem that I described.

morzlee
02-03-2023, 01:58
This entensions seems will cause performance issue.
If my server use this extension, server frames will huge shake while si spawn(like 6 si spawn very quickly)

HarryPotter
05-18-2023, 14:36
after l4d2 update

L 05/19/2023 - 02:32:16: [Actions] Failed respond to the request "HunterIntention::Reset"
L 05/19/2023 - 02:32:16: [Actions] Failed to find address for "HunterIntention::Reset" key. Check your gamedata...
L 05/19/2023 - 02:32:16: [Actions] Failed respond to the request "BoomerIntention::Reset"
L 05/19/2023 - 02:32:16: [Actions] Failed to find address for "BoomerIntention::Reset" key. Check your gamedata...
L 05/19/2023 - 02:32:16: [Actions] Failed respond to the request "TankIntention::Reset"
L 05/19/2023 - 02:32:16: [Actions] Failed to find address for "TankIntention::Reset" key. Check your gamedata...
L 05/19/2023 - 02:32:16: [Actions] Failed respond to the request "SmokerIntention::Reset"
L 05/19/2023 - 02:32:16: [Actions] Failed to find address for "SmokerIntention::Reset" key. Check your gamedata...
L 05/19/2023 - 02:32:16: [Actions] Failed respond to the request "ChargerIntention::Reset"
L 05/19/2023 - 02:32:16: [Actions] Failed to find address for "ChargerIntention::Reset" key. Check your gamedata...
L 05/19/2023 - 02:32:16: [Actions] Failed respond to the request "JockeyIntention::Reset"
L 05/19/2023 - 02:32:16: [Actions] Failed to find address for "JockeyIntention::Reset" key. Check your gamedata...
L 05/19/2023 - 02:32:16: [Actions] Failed respond to the request "SpitterIntention::Reset"
L 05/19/2023 - 02:32:16: [Actions] Failed to find address for "SpitterIntention::Reset" key. Check your gamedata...

BHaType
05-19-2023, 04:24
This extension makes it impossible to use quit, exit or _restart cmds.
Server just not responding after using these commands unless you handle it manually.
OS: Ubuntu 22.04.1
Game: L4D2
Sourcemod version: 1.11.0.6927
Metamod version: 1.11.0-dev+1148

Update:
Tested it on clean windows and linux servers
Windows: had no problems with that;
Linux: the same problem that I described.

Fixed with latest update i guess. This specific bug is not related only to this extension specifically.
I had same issue without extension and even managed to find the cause of it but I was not able to find any valuable reproduce steps for it so i gave up.
Look at this. (https://github.com/alliedmodders/sourcemod/issues/1913)

This entensions seems will cause performance issue.
If my server use this extension, server frames will huge shake while si spawn(like 6 si spawn very quickly)

Unrelated I think? Need more info.

_______
Updated L4D2

- Version bumped to 3.0
- Fixed for latest update
- Added support for custom components
- StorePendingEventResult is now deprecated
- Added user data natives
- Event handlers have separate file
- Switched to ambuild


Almost all old version plugins will work. This update is backwards compatible.

HarryPotter
05-19-2023, 05:29
Test l4d2 winodws server.
No errors for now, good work :)

KadabraZz
05-20-2023, 01:49
Can someone please provide me an example of how to change the behavior? I would like the witch as soon as it was spawned to change from the witchidle state to witchangry

BHaType
05-20-2023, 03:42
Can someone please provide me an example of how to change the behavior? I would like the witch as soon as it was spawned to change from the witchidle state to witchangry

Well, here is simple example to make her always be at 100% rage.


#pragma semicolon 1
#pragma newdecls required

#include <sourcemod>
#include <sdktools>

#include <actions>

Handle g_hWitchAngry;

public void OnPluginStart()
{
/*
* Create SDKCall for WitchAngry cosnstructor
* Linux: @_ZN10WitchAngryC2Ef
*/

StartPrepSDKCall(SDKCall_Raw);
PrepSDKCall_SetSignature(SDKLibrary_Server, "\x55\x8B\xEC\x83\xEC\x08\x0F\x57\xC0\x53\x33\ xC0", 11);
PrepSDKCall_AddParameter(SDKType_Float, SDKPass_Plain);
PrepSDKCall_SetReturnInfo(SDKType_PlainOldDat a, SDKPass_Plain);
g_hWitchAngry = EndPrepSDKCall();
}

public void OnActionCreated( BehaviorAction action, int actor, const char[] name )
{
/* When witch spawns she will sit or wander around map */
if ( strcmp(name, "WitchIdle") == 0 || strcmp(name, "WitchWander") == 0 )
{
action.OnStart = OnStart;
}
/* If witch becomes angry hook update function */
else if ( strcmp(name, "WitchAngry") == 0 )
{
action.Update = Update;
}
}

public Action OnStart(BehaviorAction action, int actor, BehaviorAction priorAction, ActionResult result)
{
/* At this point WitchIdle or WitchWander is starting */
/* Create WitchAngry and change to it */
BehaviorAction angry = CreateWitchAngry(0.0);
return action.ChangeTo(angry);
}

public Action Update(BehaviorAction action, int actor, BehaviorAction priorAction, ActionResult result)
{
/* Whenever WitchAngry updates we set witch rage to 1.0 */
SetEntPropFloat(actor, Prop_Send, "m_rage", 1.0);
return Plugin_Continue;
}

BehaviorAction CreateWitchAngry(float value)
{
BehaviorAction action = ActionsManager.Allocate(0x60);
SDKCall(g_hWitchAngry, action, value);
return action;
}

thewintersoldier97
05-20-2023, 05:26
Thanks for the update. This recent update broke many mods and plugins it crazy.

sorallll
05-27-2023, 03:14
Debian 11
I ran into these errors

L 05/27/2023 - 15:03:49: SourceMod error session started
L 05/27/2023 - 15:03:49: Info (map "c2m1_highway") (file "/home/l4d2/steamcmd/l4d2/left4dead2/addons/sourcemod/logs/errors_20230527.log")
L 05/27/2023 - 15:03:49: [SM] Unable to load extension "actions.ext": /lib32/libc.so.6: version `GLIBC_2.32' not found (required by /home/l4d2/steamcmd/l4d2/left4dead2/addons/sourcemod/extensions/actions.ext.2.l4d2.so)

sorallll
05-27-2023, 16:42
Debian 11
I ran into these errors

L 05/27/2023 - 15:03:49: SourceMod error session started
L 05/27/2023 - 15:03:49: Info (map "c2m1_highway") (file "/home/l4d2/steamcmd/l4d2/left4dead2/addons/sourcemod/logs/errors_20230527.log")
L 05/27/2023 - 15:03:49: [SM] Unable to load extension "actions.ext": /lib32/libc.so.6: version `GLIBC_2.32' not found (required by /home/l4d2/steamcmd/l4d2/left4dead2/addons/sourcemod/extensions/actions.ext.2.l4d2.so)


Replaced with your latest version (https://github.com/Vinillia/actions.ext/actions/runs/5100661776) and it works fine now

BHaType
05-27-2023, 18:15
...

Thanks for report. I somehow forgot about my local server was "bundled" with newer glibc.

Mika Misori
05-29-2023, 15:34
Thanks for report. I somehow forgot about my local server was "bundled" with newer glibc.

I apologize for the stupid question, but I don't understand which version to use?

From the first post actions.ext.l4d2.zip or Release 2.6 on GitHub?
I have several servers on Linux and Windows. I tried to put GitHub Release 2.6 on a Windows server and it went crazy, it started restarting cyclically with an error:

2023-05-29 22:31:19: minidump.cc:799: ERROR: MinidumpContext x86 size mismatch, 1147 != 716
2023-05-29 22:31:19: minidump.cc:1618: ERROR: MinidumpThread cannot read context
2023-05-29 22:31:19: stackwalker.cc:205: ERROR: Can't choose a stackwalker implementation without context

BHaType
05-29-2023, 18:40
I apologize for the stupid question, but I don't understand which version to use?

From the first post actions.ext.l4d2.zip or Release 2.6 on GitHub?
I have several servers on Linux and Windows. I tried to put GitHub Release 2.6 on a Windows server and it went crazy, it started restarting cyclically with an error:

2023-05-29 22:31:19: minidump.cc:799: ERROR: MinidumpContext x86 size mismatch, 1147 != 716
2023-05-29 22:31:19: minidump.cc:1618: ERROR: MinidumpThread cannot read context
2023-05-29 22:31:19: stackwalker.cc:205: ERROR: Can't choose a stackwalker implementation without context


Sorry for this confusion. Latest versions are always attached in first post of this thread.
GitHub is for those who need to see source code or compile extension locally for whatever reason.

Mika Misori
05-30-2023, 11:35
Sorry for this confusion. Latest versions are always attached in first post of this thread.
GitHub is for those who need to see source code or compile extension locally for whatever reason.

Thanks for the quick response and clarification.

Well, i download actions.ext.l4d2.zip from the first post and extracted it on my servers.
On Windows servers everything is fine, no errors.

But on Linux servers (I don't know the exact build of Linux, because it's hosting) give an error:

L 05/30/2023 - 17:40:58: SourceMod error session started
L 05/30/2023 - 17:40:58: Info (map "c1m2_streets") (file "/game/left4dead2/addons/sourcemod/logs/errors_20230530.log")
L 05/30/2023 - 17:40:58: [SM] Unable to load extension "actions.ext": /lib/libm.so.6: version `GLIBC_2.29' not found (required by /game/left4dead2/addons/sourcemod/extensions/actions.ext.2.l4d2.so)
L 05/30/2023 - 17:41:12: SourceMod error session started
L 05/30/2023 - 17:41:12: Info (map "c1m2_streets") (file "/game/left4dead2/addons/sourcemod/logs/errors_20230530.log")
L 05/30/2023 - 17:41:12: [SM] Unable to load extension "actions.ext": /lib/libm.so.6: version `GLIBC_2.29' not found (required by /game/left4dead2/addons/sourcemod/extensions/actions.ext.2.l4d2.so)
L 05/30/2023 - 17:41:27: Error log file session closed.
L 05/30/2023 - 17:47:18: SourceMod error session started
L 05/30/2023 - 17:47:18: Info (map "c1m2_streets") (file "/game/left4dead2/addons/sourcemod/logs/errors_20230530.log")
L 05/30/2023 - 17:47:18: [SM] Unable to load extension "actions.ext": /lib/libm.so.6: version `GLIBC_2.29' not found (required by /game/left4dead2/addons/sourcemod/extensions/actions.ext.2.l4d2.so)
L 05/30/2023 - 17:47:34: Error log file session closed.


Servers, SourceMod, MetaMod and Left 4 DHooks updated, all the latest version.

Can you please help me? Do I need to additionally download something from GitHub for the Linux servers?

Ja-Forces
05-30-2023, 16:56
You need to make sure that correct version of GLIBC is installed on hosting/server, in this case version 2.9, or ask source code for self-assembly. This build works for you? https://github.com/Vinillia/actions.ext/actions/runs/5100661776

BHaType
05-30-2023, 18:29
...

This is my fault. I used ubuntu 20 and thought the most lowest glibc for dedicated servers was GLIBC_3.XX.
Try attached version. It was compiled on ubuntu 18 and should resolve your issue.

You need to make sure that the correct version of GLIBC is installed on the hosting/server, in this case version 2.9, or ask source code for self-assembly. This build works for you? https://github.com/Vinillia/actions.ext/actions/runs/5100661776

Even though it was compiled on older ubuntu it still requires GLIBC_2.29.

objdump -T ./actions.ext.2.l4d2.so | grep -Eo 'GLIBC_\S+' | sort -u
GLIBC_2.0
GLIBC_2.1
GLIBC_2.1.3
GLIBC_2.2
GLIBC_2.2.4
GLIBC_2.29
GLIBC_2.3
GLIBC_2.3.4
GLIBC_2.4

Mika Misori
06-02-2023, 15:22
Try attached version. It was compiled on ubuntu 18 and should resolve your issue.

Thank you so much! I uploaded the version from your attachment to my Linux servers and it looks like the errors are gone since then. I'm monitoring for now, if anything goes wrong, I'll report back here.

Thanks again!

Kanashimi
06-19-2023, 00:42
Extension provides a natives to hook action event handlers and create custom actions

Notes

If two different plugins will try to return different action for same eventhandler last will be chosen.
Extension doesn't support late load so after reload you must recreate nextbots
All actions are cached by their parent action event handlers


Commands & ConVars

/* Commands */
ext_actions_dump - dumps entities actions
ext_actions_offsets - prints every hooked function offset
ext_actions_listeners - dumps actions listeners
ext_actions_list - dumps every action that manager currently holds

/* ConVars */
ext_actions_debug - debugs action propagation (1 - Enable debug, 0 - Disable debug)
ext_actions_debug_processors - Logs processors (-2 - Disabled, -1 - Debug all, N - function vtable index to debug)





#pragma semicolon 1
#pragma newdecls required

#include <sourcemod>
#include <sdktools>

#include <actions>

public void OnActionCreated( BehaviorAction action, int actor, const char[] name )
{
if ( strcmp(name, "WitchAttack") == 0 )
{
/* Hook OnStart handler */
action.OnStart = OnStart;
}
}

public Action OnStart( BehaviorAction action, int actor, BehaviorAction priorAction, ActionResult result )
{
/* When WitchAttack actions starts force it to end */
result.type = DONE;
return Plugin_Changed;
}



#pragma semicolon 1
#pragma newdecls required

#include <sourcemod>
#include <sdktools>

#include <actions>

public void OnActionCreated( BehaviorAction action, int actor, const char[] name )
{
if ( strcmp(name, "SurvivorAttack") == 0 )
action.OnStart = OnStart;
}

public Action OnStart( BehaviorAction action, int actor, BehaviorAction priorAction, ActionResult result )
{
/* We suspend WitchAttack action for our action */
/* That means WitchAttack will be like in frozen state */
/* It's will not be updated until our action is done */

return action.SuspendFor(MyCustomAction(), "Suspend example");
}

BehaviorAction MyCustomAction()
{
/* Creating action */
BehaviorAction action = ActionsManager.Create("MyCustomAction");

/* Setup event handlers */
action.OnStart = OnMyActionStart;
action.OnUpdate = OnMyActionUpdate;

return action;
}

public Action OnMyActionStart( BehaviorAction action, int actor, BehaviorAction priorAction, ActionResult result )
{
PrintToServer("We are started!");
return Plugin_Continue;
}

public Action OnMyActionUpdate( BehaviorAction action, int actor, float interval, ActionResult result )
{
PrintToServer("MyCustomAction is updating...");

if (GetRandomFloat() >= 0.5)
{
return action.Done("We are done");
}

return Plugin_Continue;
}



#pragma semicolon 1
#pragma newdecls required

#include <sourcemod>
#include <sdktools>

#include <actions>

public void OnActionCreated( BehaviorAction action, int actor, int actor, const char[] name )
{
if ( strcmp(name, "WitchIdle") == 0 || strcmp(name, "WitchAngry") == 0 )
{
/* We set post hook otherwise ActionDesiredResult will contain default values */
action.OnShovedPost = OnShovedPost;
}
}

public Action OnShovedPost( BehaviorAction action, int actor, int shover, ActionDesiredResult result )
{
if ( result.type == SUSPEND_FOR )
{
result.type = CONTINUE;
return Plugin_Changed;
}

return Plugin_Continue;
}


#pragma semicolon 1
#pragma newdecls required

#include <sourcemod>
#include <sdktools>

#include <actions>

stock int m_bIsOnThirdStrike;

public void OnPluginStart()
{
m_bIsOnThirdStrike = FindSendPropInfo("CTerrorPlayer", "m_bIsOnThirdStrike");
}

public void OnActionCreated( BehaviorAction action, int actor, const char[] name )
{
/* Hooking self healing action (when bot wants to heal self) */
if ( strcmp(name, "SurvivorHealSelf") == 0 )
action.OnStart = OnSelfAction;

/* Hooking friend healing action (when bot wants to heal someone) */
if ( strcmp(name, "SurvivorHealFriend") == 0 )
action.OnStartPost = OnFriendAction;

/* Hooking take pills action (when bot wants to take pills) */
if ( strcmp(name, "SurvivorTakePills") == 0 )
action.OnStart = OnSelfAction;

/* Hooking give pills action (when bot wants to give pills) */
if ( strcmp(name, "SurvivorGivePillsToFriend") == 0 )
action.OnStartPost = OnFriendAction;
}

public Action OnSelfAction( BehaviorAction action, int actor, BehaviorAction priorAction, ActionResult result )
{
/* When bot will be about to start healing/taking pills we chech if he's black & white */
/* if he is then we allow to heal otherwise no */
result.type = GetEntData(action.Actor, m_bIsOnThirdStrike, 1) ? CONTINUE : DONE;
return Plugin_Handled;
}

public Action OnFriendAction( BehaviorAction action, int actor, BehaviorAction priorAction, ActionResult result )
{
/* When bot will be about to start healing/giving pills to someone, we chech if friend is black & white */
/* if friend is then we allow to give heal otherwise no */
int target = action.Get(0x34) & 0xFFF;
result.type = GetEntData(target, m_bIsOnThirdStrike, 1) ? CONTINUE : DONE;
return Plugin_Handled;
}


Infected Stumble (https://forums.alliedmods.net/showthread.php?t=342823)


Source code (https://github.com/Vinillia/actions.ext.git)

I am having a issue with this. Actions completely chokes my server, making the server unplayable. I'm using Linux for my server, and it doesn't handle well.

BHaType
06-19-2023, 02:21
I am having a issue with this. Actions completely chokes my server, making the server unplayable. I'm using Linux for my server, and it doesn't handle well.

This extension doesn't do anything heavy-weighted on it's own. It's either plugin or something.

Mystik Spiral
07-03-2023, 18:50
Hi BHaType!

When a behavioral action is created, is it possible to change it to a different behavioral action? In one of your examples, you have this example where I took just an excerpt:


public void OnActionCreated( BehaviorAction action, int actor, const char[] name )
{
/* Hooking self healing action (when bot wants to heal self) */
if ( strcmp(name, "SurvivorHealSelf") == 0 )
action.OnStart = OnSelfAction;

/* Hooking take pills action (when bot wants to take pills) */
if ( strcmp(name, "SurvivorTakePills") == 0 )
action.OnStart = OnSelfAction;
}

public Action OnSelfAction( BehaviorAction action, int actor, BehaviorAction priorAction, ActionResult result )
{
/* When bot will be about to start healing/taking pills we chech if he's black & white */
/* if he is then we allow to heal otherwise no */
result.type = GetEntData(action.Actor, m_bIsOnThirdStrike, 1) ? CONTINUE : DONE;
return Plugin_Handled;
}


When the action is "SurvivorHealSelf" is it possible to change it to "SurvivorTakePills" instead, so if allowed and returned with Plugin_Changed the bot would take pills instead of healing with a first_aid_kit (assuming the bot has pain_pills of course)? I suspect the answer is No, but on the off chance the answer is Yes, please provide some information about how to configure this. Thank you!

BHaType
07-05-2023, 00:01
Hi BHaType!

When a behavioral action is created, is it possible to change it to a different behavioral action?

When the action is "SurvivorHealSelf" is it possible to change it to "SurvivorTakePills" instead, so if allowed and returned with Plugin_Changed the bot would take pills instead of healing with a first_aid_kit (assuming the bot has pain_pills of course)? I suspect the answer is No, but on the off chance the answer is Yes, please provide some information about how to configure this. Thank you!

Yeah you can create your own action with custom logic or use game action and change to it.
I created a simple example with some explanation.
https://pastebin.com/NCE0491B

You can control action transition by using natives (https://github.com/Vinillia/actions.ext/blob/707e7a9dc7976181de0cb098a38efa725c853876/sourcemod/include/actions.inc#L386-L466) and control original code execution by return value.

Plugin_Handled - Blocks original function and uses your result
Plugin_Changed - Calls original function and uses your result
Plugin_Continue - Doesn't do anything

Mystik Spiral
07-05-2023, 08:54
Yeah you can create your own action with custom logic or use game action and change to it.
I created a simple example with some explanation.
https://pastebin.com/NCE0491B

You can control action transition by using natives (https://github.com/Vinillia/actions.ext/blob/707e7a9dc7976181de0cb098a38efa725c853876/sourcemod/include/actions.inc#L386-L466) and control original code execution by return value.

Plugin_Handled - Blocks original function and uses your result
Plugin_Changed - Calls original function and uses your result
Plugin_Continue - Doesn't do anything

This was super helpful @BHaType, thank you very much!

"Actions" are all new to me so it may take a while to digest, but I believe this will let me finish what I was working on. Thank you again for such a detailed and helpful reply!

KoMiKoZa
07-19-2023, 17:09
Installed the freshest version and got this in the logs:
L 07/19/2023 - 22:01:24: [SM] Unable to load extension "actions.ext": /lib/i386-linux-gnu/libm.so.6: version `GLIBC_2.29' not found (required by /usr/local/gpx/users/komikozovich/178.63.13.143-27370/222860/left4dead2/addons/sourcemod/extensions/actions.ext.2.l4d2.so)
L 07/19/2023 - 22:01:24: [SM] Unable to load plugin "l4d2_shove_fix.smx": Required extension "Actions" file("actions.ext") not running

UPD: Ok, I see a similar post back there, could you please post a l4d2 version of that?

Dragokas
07-19-2023, 17:21
@KoMiKoZa, need to re-compile on Debian 9, or update server OS.

KoMiKoZa
07-23-2023, 19:40
@KoMiKoZa, need to re-compile on Debian 9, or update server OS.

I see, thanks. Well, I'd be glad if anyone could post the compiled version here.

Renting a server, don't think the support is going to bother with that, but might be worth a try asking. Myself, I am clueless as to how anything beyond Windows OS works, so...

Put back the backups of shove_fix.lmx and actions.l4d2.ext, works fine. Will do until then.

BHaType
07-23-2023, 22:04
Installed the freshest version and got this in the logs:

UPD: Ok, I see a similar post back there, could you please post a l4d2 version of that?

I'll update topic later with Left 4 Dead 1 update to 3.0 version.

I see, thanks. Well, I'd be glad if anyone could post the compiled version here.

As Dragokas said, it has to be compiled against older ubuntu like this one (https://forums.alliedmods.net/showpost.php?p=2805288&postcount=80) which should resolve your issue.

KoMiKoZa
07-24-2023, 18:20
like this one (https://forums.alliedmods.net/showpost.php?p=2805288&postcount=80) which should resolve your issue.
Oof. I thought this was for L4D1 because of its name, hence why I asked for the L4D2 version. Yeah, that works alright, thanks! :bee:

awesome144
09-18-2023, 00:47
Extension fails to load on Team Fortress 2, gamedata probably outdated since the December 2022 update.

BHaType
09-19-2023, 00:13
Extension fails to load on Team Fortress 2, gamedata probably outdated since the December 2022 update.

Yeah I know. Latest TF2 released version is hard-outdated but I almost finished work for new version with features matching with L4D2 branch.
I attached my latest TF2 build. Basically it's the same version as L4D2 build but I didn't test it so I can't guarantee it to work properly especially with new event handlers.

BHaType
09-24-2023, 03:43
Updated

- Bumped Team Fortress to match latest branch
- Bumped Left 4 Dead to match latest branch

Proaxel
09-25-2023, 14:28
Updated

- Bumped Team Fortress to match latest branch
- Bumped Left 4 Dead to match latest branch


I installed this extension in order to use "Fix Common Shove" by jensewe (https://github.com/Target5150/MoYu_Server_Stupid_Plugins/tree/master/The%20Last%20Stand/l4d_fix_common_shove). At first everything seemed fine, but then I woke up the next day to the server crashing as soon as anyone joins and over 40+ of this same crash report on the dashboard: https://crash.limetech.org/tz7hcbgc7xra

Disabling the aforementioned plugin was not sufficient, I had to unload this extension in order for anyone to join again.

BHaType
09-25-2023, 21:07
I installed this extension in order to use "Fix Common Shove" by jensewe (https://github.com/Target5150/MoYu_Server_Stupid_Plugins/tree/master/The%20Last%20Stand/l4d_fix_common_shove). At first everything seemed fine, but then I woke up the next day to the server crashing as soon as anyone joins and over 40+ of this same crash report on the dashboard: https://crash.limetech.org/tz7hcbgc7xra

Disabling the aforementioned plugin was not sufficient, I had to unload this extension in order for anyone to join again.

Sorry for this inconvenience. There was a regression that caused linux build to crash server. Should be fixed now.

awesome144
09-30-2023, 05:31
Updated

- Bumped Team Fortress to match latest branch
- Bumped Left 4 Dead to match latest branch


Thanks for the update. The question I have though is how would I change to an existing game's action if there's no "constructor" for it?

BHaType
09-30-2023, 07:21
Thanks for the update. The question I have though is how would I change to an existing game's action if there's no "constructor" for it?

There is always will be constructor but it can be inlined and unfortuantely in that case you can't do much except some hard memory patching and thunk memory blocks.
On the other hand TF2 SDK has every nextbot's action so you can easliy recreate them (link click (https://github.com/OthmanAba/TeamFortress2/tree/1b81dded673d49adebf4d0958e52236ecc28a956/tf2_src/game/server/tf/bot/behavior)).

awesome144
09-30-2023, 22:33
There is always will be constructor but it can be inlined and unfortuantely in that case you can't do much except some hard memory patching and thunk memory blocks.
On the other hand TF2 SDK has every nextbot's action so you can easliy recreate them (link click (https://github.com/OthmanAba/TeamFortress2/tree/1b81dded673d49adebf4d0958e52236ecc28a956/tf2_src/game/server/tf/bot/behavior)).

I think that's a 2017 build, and I was hoping I wouldn't have to do something like that, but oh well.

ReCreator
10-01-2023, 12:45
After installing latest actions(from main page) server crashes on 1 client connect.
L4D1, SM 1.11, Linux.

BHaType
10-01-2023, 19:42
After installing latest actions(from main page) server crashes on 1 client connect.
L4D1, SM 1.11, Linux.

Thanks for report

Updated

- Fixed Left 4 Dead linux crash

foxhound27
10-03-2023, 22:53
Hello bro, thank you for this great work, but it's not working for me

Error:

[21] Actions (3.6.2): Nextbot action tree manager
[22] <FAILED> file "actions.ext.2.l4d2.so": Failed to create handle type (error: 10)

Using:
Game: L4D2
OS: Linux
SourceMod Version: 1.11.0.6840
Metamod:Source version 1.11.0-dev+1145

HarryPotter
10-04-2023, 02:12
Error:

[21] Actions (3.6.2): Nextbot action tree manager
[22] <FAILED> file "actions.ext.2.l4d2.so": Failed to create handle type (error: 10)



why there are two actions?

BHaType
10-04-2023, 07:36
Hello bro, thank you for this great work, but it's not working for me
[21] Actions (3.6.2): Nextbot action tree manager
[22] <FAILED> file "actions.ext.2.l4d2.so": Failed to create handle type (error: 10)

You are trying to load 2 'actions' extensions. That's illegal.

foxhound27
10-04-2023, 14:02
i had only these 2 files actions autoload and actions.ext.2.l4d2.so, it seems my server was crazy and needed a restart, thank you! now all is fine

awesome144
10-31-2023, 06:48
So I'm a little perplexed on how the InitialContainedAction callback works. Changing the third parameter with a different return allows a different action to be the child action, but I don't really know what the purpose of the first parameter is for. Also I'm wondering is it possible to get the action it should return (assuming the action it hooked uses InitialContainedAction), if that has to be done using the post hook.

BHaType
11-02-2023, 21:41
So I'm a little perplexed on how the InitialContainedAction callback works.

Action calls InitialContainedAction to create optional child action and invoked before OnStart of calle action also child action always gets updates first.

Changing the third parameter with a different return allows a different action to be the child action, but I don't really know what the purpose of the first parameter is for.

That's was include issue, I just pushed update to remove this confusion (InitialContainedAction (https://github.com/Vinillia/actions.ext/blob/51f56c12f6c4304ee0a372086598cba5e4e79e82/sourcemod/include/actions_processors.inc#L84C54-L84C54)).
First two params are basicly same for all non contextual handlers. The 3rd one represents return value of InitialContainedAction handler.

Also I'm wondering is it possible to get the action it should return (assuming the action it hooked uses InitialContainedAction), if that has to be done using the post hook.

Yep, simply hook InitialContainedActionPost and then child param will be actual return value of original IntialContainedAction.

BHaType
11-02-2023, 21:44
Updated

- Renamed 3rd param of InitialContainedAction to more appropriate name
- Simplified InitialContainedAction child param routine (before you had to wait 1 frame to use it)

BHaType
11-03-2023, 16:02
Updated

- Fixed crash caused by prior update

Mystik Spiral
01-16-2024, 18:58
Hi BHaType!

In this post (https://forums.alliedmods.net/showpost.php?p=2806809&postcount=84), you provided this example (https://pastebin.com/NCE0491B). On line 42 of that example is this text:

PrepSDKCall_SetVirtual(284); // 285 linux

Does this mean I need to change 284 to 285 if my code is running on Linux instead of Windows?

I realize that is an index (vtblidx), but I am not sure what it is an index to. There are still many things for me to learn about SourceMod so I apologize if my question seems "basic".

If that is what I need to do, since my plugin also uses Left4DHooksDirect, I thought I would try to use GetOSType to set the value appropriately.

BHaType
01-18-2024, 15:21
Does this mean I need to change 284 to 285 if my code is running on Linux instead of Windows?


Yes, if you don't change it then plugin will call wrong function which most likely cause a crash.


I realize that is an index (vtblidx), but I am not sure what it is an index to. There are still many things for me to learn about SourceMod so I apologize if my question seems "basic".


There is a special keyword in C++ to store function in an array with other functions with same keyword.
If you need to call specific function from that array then you need to know index of the function you want to call.
You can decompile appropriate binary and calculate index by yourself or use VTable Dumper by asherkin (https://asherkin.github.io/vtable/).

In your case 284 is an index of "Weapon_Switch" from "CTerrorPlayer" array.
Those indexes are different between Windows and Linux because afaik this is compiler specific stuff.

This is extremely simplified explanation.


If that is what I need to do, since my plugin also uses Left4DHooksDirect, I thought I would try to use GetOSType to set the value appropriately.

There is nothing wrong with GetOSType but actually it's a bad practice.
Anything platform/game specific or ambiguous data is always better to cover up into gamedata if possible.

Mystik Spiral
01-19-2024, 07:38
Great info. Thank you @BHaType!

awesome144
02-16-2024, 03:32
SelectMoreDangerousThreat is an optional native, but it doesn't have its own property in the methodmap? Also could I get an explanation of its callback function?

caxanga334
02-28-2024, 19:02
SelectMoreDangerousThreat is an optional native, but it doesn't have its own property in the methodmap? Also could I get an explanation of its callback function?

When IVision::GetPrimaryKnownThreat is called, IContextualQuery::SelectMoreDangerousThreat is used to filter/select which Known Entity the bot should consider as it's primary threat. Generally used when selecting targets to attack.

IContextualQuery::SelectMoreDangerousThreat returns a CKnownEntity pointer and has 4 parameters: The bot itself, the bot BCC, and two known entities, threat1 and threat2. The function must return if the bot should focus on threat1 or threat2. NULL can be returned for a 'no answer'.

In the extension: function Action (any action, Address nextbot, int entity, Address threat1, Address threat2, Address& knownEntity). You have the address to threat1 and threat2. The result is stored in the last parameter: Address& knownEntity.

little_froy
03-09-2024, 23:54
L4D2 how can I get attack target in SurvivorAttack? I want to prevent some attacks.

Solved: https://forums.alliedmods.net/showthread.php?t=346696

yongeni
03-11-2024, 09:10
!It will lead to many three -way map fried

0 server_srv.so!PathFollower::LadderUpdate(INex tBot*) + 0x251
1 server_srv.so!PathFollower::Update(INextBot*) + 0x273
2 server_srv.so!ChaseVictim::Update(Infected*, float) + 0x546
3 actions.ext.2.l4d2.so!hexecution<[...]> ProcessHandlerImpl<[...]>(ProcessHandlerEx<[...]>(unsigned int, ActionProcessorShared*, ActionResult<[...]> (ActionProcessorShared::*&&)(CBaseEntity*, float), CBaseEntity*&, float&)::{lambda()#1}&, Action<[...]>*, unsigned int, ActionResult<[...]> (ActionProcessorShared::*&)(CBaseEntity*, float), CBaseEntity*&, float&) [invoke.h:73 + 0x1a]
4 actions.ext.2.l4d2.so!ActionProcessorShared:: Update(CBaseEntity*, float) [actions_processor_shared.h:178 + 0x10]
5 server_srv.so!Action<Infected>::InvokeUpdate(Infected*, Behavior<Infected>*, float) + 0xed
6 server_srv.so!Action<Infected>::InvokeUpdate(Infected*, Behavior<Infected>*, float) + 0x12a
7 server_srv.so!Action<Infected>::InvokeUpdate(Infected*, Behavior<Infected>*, float) + 0x12a
8 server_srv.so!Action<Infected>::InvokeUpdate(Infected*, Behavior<Infected>*, float) + 0x12a
9 server_srv.so!Behavior<Infected>::Update(Infected*, float) + 0x55
10 server_srv.so!InfectedIntention::Update() + 0xd4
11 server_srv.so!INextBot::Update() + 0x84
12 server_srv.so!Infected::Update() + 0xc5
13 server_srv.so!NextBotCombatCharacter::DoThink () + 0x274
14 server_srv.so!CBaseEntity::PhysicsDispatchThi nk(void (CBaseEntity::*)()) + 0xa9
15 server_srv.so!CBaseEntity::PhysicsRunSpecific Think(int, void (CBaseEntity::*)()) + 0xc4
16 server_srv.so!_ZN11CBaseEntity15PhysicsRunThi nkENS_14thinkmethods_tE.part.150 + 0x32
17 server_srv.so!CBaseEntity::PhysicsCustom() + 0xe7
18 server_srv.so!CBaseEntity::PhysicsSimulate() + 0x9e0
19 server_srv.so!Physics_SimulateEntity(CBaseEnt ity*) + 0x152
20 server_srv.so!Physics_RunThinkFunctions(bool) + 0x2d1
21 server_srv.so!CServerGameDLL::GameFrame(bool) + 0x967
22 sourcemod.2.l4d2.so!__SourceHook_FHCls_IServe rGameDLLGameFramefalse::Func + 0x9b
23 engine_srv.so!SV_Think(bool) + 0x1c8
24 engine_srv.so!SV_Frame(bool) + 0x168
25 engine_srv.so!_Host_RunFrame_Server(bool) + 0x17c
26 engine_srv.so!_Host_RunFrame(float) + 0x470
27 engine_srv.so!CHostState::State_Run(float) + 0xf8
28 engine_srv.so!CHostState::FrameUpdate(float) + 0x166
29 engine_srv.so!HostState_Frame(float) + 0x1d
30 engine_srv.so!CEngine::Frame() + 0x54f
31 engine_srv.so!CDedicatedServerAPI::RunFrame() + 0x26
32 dedicated_srv.so!RunServerIteration(bool) + 0x3c
33 dedicated_srv.so!RunServer(bool) + 0x48
34 engine_srv.so!CModAppSystemGroup::Main() + 0x8d
35 engine_srv.so!CAppSystemGroup::Run() + 0x38
36 engine_srv.so!CDedicatedServerAPI::ModInit(Mo dInfo_t&) + 0x1af
37 dedicated_srv.so!CDedicatedAppSystemGroup::Ma in() + 0xb5
38 dedicated_srv.so!CAppSystemGroup::Run() + 0x38
39 dedicated_srv.so!CAppSystemGroup::Run() + 0x38
40 dedicated_srv.so!main + 0x217
41 srcds_linux!main + 0xf2
42 libc-2.27.so!__libc_start_main + 0xf1
43 srcds_linux + 0x885
44 srcds_linux + 0x630
45 srcds_linux + 0x950
46 srcds_linux + 0x9c0

little_froy
03-12-2024, 05:13
report: L4D2 SurvivorAttack.SelectTargetPoint can't be called if plugin late load.

BHaType
04-23-2024, 16:20
SelectMoreDangerousThreat is an optional native, but it doesn't have its own property in the methodmap? Also could I get an explanation of its callback function?
I forgot to add them, fixed in the latest update.

edit:
that callback is game depended because in l4d2 and tf2 it seems to act different
but judging by the source code (https://github.com/lua9520/source-engine-2018-hl2_src/blob/3bf9df6b2785fa6d951086978a3e66f49427166a/game/server/NextBot/NextBotVisionInterface.cpp#L59-L113) it's just a way to select either the first threat on the list or SelectMoreDangerousThreat result.
basicly what @caxanga334 said

L4D2 how can I get attack target in SurvivorAttack? I want to prevent some attacks.
Solved: https://forums.alliedmods.net/showthread.php?t=346696
You have to reverse "SurvivorAttack" action to get survivor's target

...
This is known crash and has nothing to do with the extension.

report: L4D2 SurvivorAttack.SelectTargetPoint can't be called if plugin late load.
?

BHaType
04-23-2024, 16:22
Updated

- Fixed memory leak for sub-overridden actions
- Added vector memory property functions
- Added vector/string userdata support
- Added missing contextual handlers (thanks to OfficerSpy)
- Added new ActionConstructor type to setup and call action constructors within gamedata