diff --git a/docs/events.md b/docs/events.md index 8241379..d95c05d 100644 --- a/docs/events.md +++ b/docs/events.md @@ -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 diff --git a/src/core/core_i_framework.nss b/src/core/core_i_framework.nss index b62cc5b..c2f2cea 100644 --- a/src/core/core_i_framework.nss +++ b/src/core/core_i_framework.nss @@ -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) @@ -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 @@ -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;