Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Plugin event|library script registration via funtion decorator #47

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions docs/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,34 @@ game events for the module, AoEs, areas, doors, encounters, placeables, stores,
triggers, traps, and creatures. PCs also have their own creature events that use
the prefix `OnPC*` instead of `OnCreature*`.

Scripts can also be registered using the `RegisterPluginScripts()` function.
This function takes the [object to register the script on](#script-sources) and
a CSV list of glob patterns matching nss filenames. This function requires
a specified decorator be applied to each event script in a script library.
The decorator must reside in a comment immediately preceding the specific
function and must contain the following:

- Decorator (required): `@EVENT[...]`
- Event Reference (required): `OnModuleLoad`
- [Priority](#script-priorities) (optional)

Example decorators:

```c
// @EVENT[OnModuleLoad:first]
void pw_OnModuleLoad() {...}

// @EVENT[OnPlayerDeath]
void pw_OnPlayerDeath() {...}

// @EVENT[OnPlayerChat:3.0]
void pw_OnPlayerChat() {...}
```

While registering scripts decorated with `@EVENT[...]`, `RegisterPluginScripts()`
will also register any library scripts decorated with `@LIBRARY[]` in script
libraries matching the given glob patterns.

## Script Sources
Since scripts are not placed directly into an object's event script slots, the
framework needs to know what scripts should be run for an event. There are
Expand Down
81 changes: 80 additions & 1 deletion src/core/core_i_framework.nss
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,22 @@ void ClearEventState(string sEvent = "");
/// whether oTarget is a plugin or other object.
void RegisterEventScript(object oTarget, string sEvent, string sScripts, float fPriority = -1.0);

/// @brief Register all scripts decoarted with @EVENT[...] in any nss file
/// matching the given glob patterns.
/// @param oPlugin A plugin's data object.
/// @param sPatterns A CSV list of glob patterns to match. Supported syntax:
/// - `*`: match zero or more characters
/// - `?`: match a single character
/// - `[abc]` : match any of a, b, or c
/// - `[a-z]` : match any character from a-z
/// - other text is matched literally
/// @param bLoadLibraries If TRUE, will register all scripts decorated with
/// with @LIBRARY[].
/// @note Scripts marked with @EVENT[...] will automatically be registered
/// as library scripts, only use @LIBRARY[] for library scripts not
/// associated with events.
void RegisterPluginScripts(object oPlugin, string sPatterns, int bLoadLibraries = TRUE);

/// @brief Run an event, causing all subscribed scripts to trigger.
/// @param sEvent The name of the event
/// @param oInit The object triggering the event (e.g, a PC OnClientEnter)
Expand Down Expand Up @@ -590,12 +606,73 @@ void RegisterEventScript(object oTarget, string sEvent, string sScripts, float f
}
}

// Alias function for backward compatibility.
void RegisterEventScripts(object oTarget, string sEvent, string sScripts, float fPriority = -1.0)
{
RegisterEventScript(oTarget, sEvent, sScripts, fPriority);
}

void DumpEventScripts(string sEvent, int bSearchAll = FALSE)
{

}

void RegisterPluginScripts(object oPlugin, string sPatterns, int bLoadLibraries = TRUE)
{
Debug("Loading plugin script libraries matching \"" + sPatterns + "\"");

json jLibraries, jScripts;
if ((jLibraries = FilterByPatterns(_GetScriptsByPrefix(JSON_ARRAY, "", RESTYPE_NSS), ListToJson(sPatterns))) == JSON_ARRAY)
{
Debug("No plugin script libraries found matching \"" + sPatterns + "\"");
return;
}

string sPriorities = "first|only|last|default";
string sReturns = "void|int|string|json|object|float|sqlquery|talent|effect|location|vector|cassowary|itemproperty";
json jAlternates = ListToJson(sPriorities + "," + sReturns);

string sEventRegex = SubstituteString("@EVENT\\[([ -~]*?):?(10\\.0|\\d{1}\\.\\d{1}|$1)?\\](?:.|\\r?\\n)*?(?:$2)\\s+([a-z_]\\w*)\\s*\\(", jAlternates);
string sLibRegex = SubstituteString("@LIBRARY\\[\\](?:.|\\r?\\n)*?(?:$2)\\s+([a-z_]\\w*)\\s*\\(", jAlternates);

int i; for (i; i < JsonGetLength(jLibraries); i++)
{
string sLibrary = JsonGetString(JsonArrayGet(jLibraries, i));
string sContent = ResManGetFileContents(sLibrary, RESTYPE_NSS);
if (sContent == "")
{
Debug("Unable to retrieve contents of " + sLibrary + ".nss");
continue;
}

if ((jScripts = RegExpIterate(sEventRegex, sContent)) == JSON_ARRAY)
Debug("No @EVENT[...] decorators found in " + sLibrary + ".nss");
else
{
int n; for (n; n < JsonGetLength(jScripts); n++)
{
string sIndex = IntToString(n);
string sEvent = JsonGetString(JsonPointer(jScripts, "/" + sIndex + "/1"));
string sScript = JsonGetString(JsonPointer(jScripts, "/" + sIndex + "/3"));
float fPriority = StringToPriority(JsonGetString(JsonPointer(jScripts, "/" + sIndex + "/2")), GLOBAL_EVENT_PRIORITY);

RegisterEventScript(oPlugin, sEvent, sScript, fPriority);
AddLibraryScript(sLibrary, sScript);
}
}

if (bLoadLibraries)
{
if ((jScripts = RegExpIterate(sLibRegex, sContent)) == JSON_ARRAY)
Debug("No @LIBRARY[] decorators found in " + sLibrary + ".nss");
else
{
int n; for (n; n < JsonGetLength(jScripts); n++)
AddLibraryScript(sLibrary, JsonGetString(JsonPointer(jScripts, "/" + IntToString(n) + "/1")));
}
}
}
}

// Private function. Checks oTarget for a builder-specified event hook string
// for sEvent and expands it into a list of scripts and priorities on oTarget.
// An event hook string is a CSV list of scripts and priorities, each specified
Expand Down Expand Up @@ -986,6 +1063,8 @@ void HookObjectEvents(object oObject, int bSkipHeartbeat = TRUE, int bStoreOldEv
nEnd = EVENT_SCRIPT_TRIGGER_ON_CLICKED;
if (bSkipHeartbeat)
nStart++;
if (JsonPointer(ObjectToJson(oObject), "/LinkedTo/value") == JsonString(""))
nEnd--;
break;
case OBJECT_TYPE_STORE:
nStart = EVENT_SCRIPT_STORE_ON_OPEN;
Expand Down