Calling AI handlers from plugins – ShiVa Engine

Calling AI handlers from plugins

There are three main ways to exchange data between plugins and AIModels:
1. Immediately through the called function itself. Functions can have multiple, single, or no return values at all. But every variable in Lua is also a reference, which means you can use the function parameters not only as input, but also as output. This is useful for data types that C++ does not have, like tables:

2. Sending data to a specific AI, either through a custom handler using S3DX::user.sendEvent(), or to a member variable using S3DX::user.setAIVariable() (deprecated) or S3DX::application.setCurrentUserAIVariable(). This requires at least some knowledge about the AIModel, like structure, available handlers and variables, as well as its name.
3. Broadcasting data to all users and all user AIModels, checking for available handlers and variables in the process. This gives you the most flexibility, but will be the most performance hungry variant.
Option 1 is straight forward. This article will look at the other two options.

Named AIs

The easiest way to create a link between an AIModel and your plugin is by transmitting the name of your target AIModel in the function init() call:


where “targetAIModelName” is the name of an AIModel in the user stack with all your custom handlers and AIVariables needed for communication with the plugin. The corresponding C++ function could look something like this:

// myplug.cpp, generated by ShiVa
int Callback_myplug_init ( int _iInCount, const S3DX::AIVariable *_pIn, S3DX::AIVariable *_pOut )
    S3DX_API_PROFILING_START( "myplug.init" ) ;
    // Input Parameters
    int iInputCount = 0 ;
    S3DX::AIVariable sAIModel = ( iInputCount < _iInCount ) ? _pIn[iInputCount++] : S3DX::AIVariable ( ) ;
    // Output Parameters
    S3DX::AIVariable bOK ;
	// "SB" here is a unique_ptr to a custom class
	// where all our custom code is contained
	if (SB)
		bOK.SetBooleanValue (SB->init(sAIModel.GetStringValue()));
    // Return output Parameters
    int iReturnCount = 0 ;
    _pOut[iReturnCount++] = bOK ;
    return iReturnCount;
// inside our custom CPP_myplug.cpp class
const char* _sAI = "dummy";
bool CPP_myplug::init(const char * sAI) {
	if ((sAI != nullptr) && (sAI[0] != '\0'))
		_sAI = sAI;
	return true;

It is important to check the input against nil (nullptr) and it being empty (\0 terminator character) before you store it and use it. The AIModel name string can then be used directly in a sendEvent call. The conversion from const char* to AIVariable is implicit.

S3DX::user.sendEvent(S3DX::application.getCurrentUser(), _sAI, eventName, val1, val2);

Please note that the return value of S3DX::application.getCurrentUser() only lives a very short time before it becomes invalid. You cannot store it in a C++ variable at the beginning of the game and then rely on it, not even in a single player game where the user does not change. If your target is an AIModel in the current user stack, be safe and call S3DX::application.getCurrentUser() you need to use a function which uses a user handle.


The alternative to named AIs is broadcasting. The following code will reach any local user with an AIModel that carries an event handler with the name “eventStickMove” and then send a message:

void C_MFI::runFrame() {
	auto userCount = S3DX::application.getUserCount();
	if (userCount < 1) return;
	for (int j=0; j

That means you are free to move your events around, from one AI to another, which gives you a great deal of robustness and flexibility. However if you have multiple plugins which all use this looping method, you will inevitably introduce a lot of overhead any busy looping. The solution to this problem is event queuing: Put all the events you want to send into a vector/array/list/... and then have once central loop (like the one above) which works through the queue. This is very similar to how the EventQueue plugin works.

Need more answers?

  • slackBanner