Difference between revisions of "Optional Requirements (SourceMod Scripting)"
m |
Joinedsenses (talk | contribs) m (Update highlighting and formatting) |
||
(5 intermediate revisions by 5 users not shown) | |||
Line 5: | Line 5: | ||
To disable an extension being marked as "required," remove the <tt>REQUIRE_EXTENSIONS</tt> define. For example: | To disable an extension being marked as "required," remove the <tt>REQUIRE_EXTENSIONS</tt> define. For example: | ||
− | < | + | <sourcepawn>#include <sourcemod> |
#undef REQUIRE_EXTENSIONS | #undef REQUIRE_EXTENSIONS | ||
− | #include <sdktools></ | + | #include <sdktools></sourcepawn> |
Note that any extensions included after the <tt>#undef</tt> will also be marked as not required. Thus, you may need to move the include down, or do something like: | Note that any extensions included after the <tt>#undef</tt> will also be marked as not required. Thus, you may need to move the include down, or do something like: | ||
− | < | + | <sourcepawn>#include <sourcemod> |
#undef REQUIRE_EXTENSIONS | #undef REQUIRE_EXTENSIONS | ||
#include <sdktools> | #include <sdktools> | ||
− | #define REQUIRE_EXTENSIONS</ | + | #define REQUIRE_EXTENSIONS</sourcepawn> |
==Plugins== | ==Plugins== | ||
To disable an plugin being marked as "required," remove the <tt>REQUIRE_PLUGIN</tt> define. For example: | To disable an plugin being marked as "required," remove the <tt>REQUIRE_PLUGIN</tt> define. For example: | ||
− | < | + | <sourcepawn>#include <sourcemod> |
#undef REQUIRE_PLUGIN | #undef REQUIRE_PLUGIN | ||
− | #include <ircrelay></ | + | #include <ircrelay></sourcepawn> |
Note that any plugins included after the <tt>#undef</tt> will also be marked as not required. Thus, you may need to move the include down, or do something like: | Note that any plugins included after the <tt>#undef</tt> will also be marked as not required. Thus, you may need to move the include down, or do something like: | ||
− | < | + | <sourcepawn>#include <sourcemod> |
#undef REQUIRE_PLUGIN | #undef REQUIRE_PLUGIN | ||
#include <ircrelay> | #include <ircrelay> | ||
− | #define REQUIRE_PLUGIN</ | + | #define REQUIRE_PLUGIN</sourcepawn> |
==Optional Natives== | ==Optional Natives== | ||
− | To mark a native as optional, use <tt>MarkNativeAsOptional</tt>. It should be called in <tt> | + | To mark a native as optional, use <tt>MarkNativeAsOptional</tt>. It should be called in <tt>AskPluginLoad2</tt>. For example: |
− | < | + | <sourcepawn>public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) |
{ | { | ||
− | + | MarkNativeAsOptional("SDKCall"); | |
− | }</ | + | return APLRes_Success; |
− | + | }</sourcepawn> | |
=Checking Optional Dependencies= | =Checking Optional Dependencies= | ||
If you use a plugin or extension as an optional dependency, you may need to check whether it exists. For example, let's say we're relying on a plugin with the library name of "ircrelay." The way to always know whether ircrelay is loaded (and working) is: | If you use a plugin or extension as an optional dependency, you may need to check whether it exists. For example, let's say we're relying on a plugin with the library name of "ircrelay." The way to always know whether ircrelay is loaded (and working) is: | ||
− | < | + | <sourcepawn>bool ircrelay = false; |
− | public | + | public void OnAllPluginsLoaded() |
{ | { | ||
− | + | ircrelay = LibraryExists("ircrelay"); | |
} | } | ||
− | public OnLibraryRemoved(const | + | public void OnLibraryRemoved(const char[] name) |
{ | { | ||
− | + | if (StrEqual(name, "ircrelay")) | |
− | + | { | |
− | + | ircrelay = false; | |
− | + | } | |
} | } | ||
− | public OnLibraryAdded(const | + | public void OnLibraryAdded(const char[] name) |
{ | { | ||
− | + | if (StrEqual(name, "ircrelay")) | |
− | + | { | |
− | + | ircrelay = true; | |
− | + | } | |
− | }</ | + | }</sourcepawn> |
Line 69: | Line 69: | ||
Allowing other plugins to use your plugin as a library requires making an include file with two structures (the second of which is optional). The first structure must look like this: | Allowing other plugins to use your plugin as a library requires making an include file with two structures (the second of which is optional). The first structure must look like this: | ||
− | < | + | <sourcepawn>public SharedPlugin __pl_myfile = |
{ | { | ||
− | + | name = "myfile", | |
− | + | file = "myfile.smx", | |
#if defined REQUIRE_PLUGIN | #if defined REQUIRE_PLUGIN | ||
− | + | required = 1, | |
#else | #else | ||
− | + | required = 0, | |
#endif | #endif | ||
− | };</ | + | };</sourcepawn> |
The basic format is: | The basic format is: | ||
Line 87: | Line 87: | ||
Additionally, you should expose a function which marks all of your natives as optional. You can do this by: | Additionally, you should expose a function which marks all of your natives as optional. You can do this by: | ||
− | < | + | <sourcepawn>#if !defined REQUIRE_PLUGIN |
+ | public void __pl_myfile_SetNTVOptional() | ||
{ | { | ||
− | + | MarkNativeAsOptional("native1"); | |
− | + | MarkNativeAsOptional("native2"); | |
− | + | MarkNativeAsOptional("native3"); | |
− | }</ | + | } |
+ | #endif</sourcepawn> | ||
This function will be secretly called before the plugin loads (if and only if the requirement is optional), thus allowing seamless optional usage by third party developers. Note that the <tt>__pl_</tt> and <tt>_SetNTVOptional</tt> portions <b>must</b> be present, and that everything in between must match the ending of <tt>__pl_</tt> for the <tt>SharedPlugin</tt> structure. | This function will be secretly called before the plugin loads (if and only if the requirement is optional), thus allowing seamless optional usage by third party developers. Note that the <tt>__pl_</tt> and <tt>_SetNTVOptional</tt> portions <b>must</b> be present, and that everything in between must match the ending of <tt>__pl_</tt> for the <tt>SharedPlugin</tt> structure. | ||
− | You also must register the library name with SourceMod -- again this should be the unique string. This | + | You also must register the library name with SourceMod -- again this should be the unique string. This should be done inside the <code>AskPluginLoad2</code> function. |
− | < | + | <sourcepawn> |
− | public | + | public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) |
{ | { | ||
− | + | //... code here ... | |
− | + | RegPluginLibrary("myfile"); | |
+ | |||
+ | return APLRes_Success; | ||
} | } | ||
− | </ | + | </sourcepawn> |
= Full Example = | = Full Example = | ||
;''Bounty: bounty.sp'' | ;''Bounty: bounty.sp'' | ||
− | < | + | <sourcepawn> |
#include <sourcemod> | #include <sourcemod> | ||
#undef REQUIRE_PLUGIN | #undef REQUIRE_PLUGIN | ||
#include <ircrelay> | #include <ircrelay> | ||
− | </ | + | </sourcepawn> |
;''Bounty: bounty.config.sp'' | ;''Bounty: bounty.config.sp'' | ||
− | < | + | <sourcepawn> |
− | + | bool plugin_IrcRelay = LibraryExists("ircrelay"); | |
if ((BountyIRC) && (plugin_IrcRelay)) | if ((BountyIRC) && (plugin_IrcRelay)) | ||
{ | { | ||
− | + | RegisterIrcCommand("!bounty", "x", Irc_ViewBounty); | |
− | + | IrcMessage(CHAN_MASTER, "IRC Bounty Running!"); | |
− | |||
− | |||
− | |||
− | |||
− | |||
} | } | ||
− | </ | + | else |
+ | { | ||
+ | if ((BountyIRC) && (!plugin_IrcRelay)) | ||
+ | { | ||
+ | BountyConsole_Debug("%t", "Bounty IRC Relay failed"); | ||
+ | } | ||
+ | } | ||
+ | </sourcepawn> | ||
;''IRC Relay: ircrelay.sp'' | ;''IRC Relay: ircrelay.sp'' | ||
− | < | + | <sourcepawn> |
− | public | + | public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) |
{ | { | ||
− | + | //... code here ... | |
− | + | RegPluginLibrary("ircrelay"); | |
+ | |||
+ | return APLRes_Success; | ||
} | } | ||
− | </ | + | </sourcepawn> |
;''IRC Relay: ircrelay.inc'' | ;''IRC Relay: ircrelay.inc'' | ||
− | < | + | <sourcepawn> |
− | public SharedPlugin | + | public SharedPlugin __pl_ircrelay = |
{ | { | ||
− | + | name = "ircrelay", | |
− | + | file = "ircrelay.smx", | |
#if defined REQUIRE_PLUGIN | #if defined REQUIRE_PLUGIN | ||
− | + | required = 1, | |
#else | #else | ||
− | + | required = 0, | |
#endif | #endif | ||
}; | }; | ||
− | public __pl_ircrelay_SetNTVOptional() | + | #if !defined REQUIRE_PLUGIN |
+ | public void __pl_ircrelay_SetNTVOptional() | ||
{ | { | ||
− | + | MarkNativeAsOptional("IrcMessage"); | |
− | + | MarkNativeAsOptional("RegisterIrcCommand"); | |
− | + | MarkNativeAsOptional("IrcGetCmdArgc"); | |
− | + | MarkNativeAsOptional("IrcGetCmdArgv"); | |
} | } | ||
− | </ | + | #endif |
+ | </sourcepawn> | ||
The <code>name</code> value is what will be checked when you run <code>LibraryExists</code>. This allows the bounty script to fully work, even if the IRC relay plugin is not installed and/or running correctly on your server. | The <code>name</code> value is what will be checked when you run <code>LibraryExists</code>. This allows the bounty script to fully work, even if the IRC relay plugin is not installed and/or running correctly on your server. | ||
[[Category:SourceMod Scripting]] | [[Category:SourceMod Scripting]] | ||
− |
Latest revision as of 18:41, 29 March 2020
Normally, if you use natives from an extension or another plugin, your plugin will not load unless those natives exist. However, it is possible to make your dependencies "optional." This article details how.
Contents
Disabling Requirements
Extensions
To disable an extension being marked as "required," remove the REQUIRE_EXTENSIONS define. For example:
#include <sourcemod> #undef REQUIRE_EXTENSIONS #include <sdktools>
Note that any extensions included after the #undef will also be marked as not required. Thus, you may need to move the include down, or do something like:
#include <sourcemod> #undef REQUIRE_EXTENSIONS #include <sdktools> #define REQUIRE_EXTENSIONS
Plugins
To disable an plugin being marked as "required," remove the REQUIRE_PLUGIN define. For example:
#include <sourcemod> #undef REQUIRE_PLUGIN #include <ircrelay>
Note that any plugins included after the #undef will also be marked as not required. Thus, you may need to move the include down, or do something like:
#include <sourcemod> #undef REQUIRE_PLUGIN #include <ircrelay> #define REQUIRE_PLUGIN
Optional Natives
To mark a native as optional, use MarkNativeAsOptional. It should be called in AskPluginLoad2. For example:
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { MarkNativeAsOptional("SDKCall"); return APLRes_Success; }
Checking Optional Dependencies
If you use a plugin or extension as an optional dependency, you may need to check whether it exists. For example, let's say we're relying on a plugin with the library name of "ircrelay." The way to always know whether ircrelay is loaded (and working) is:
bool ircrelay = false; public void OnAllPluginsLoaded() { ircrelay = LibraryExists("ircrelay"); } public void OnLibraryRemoved(const char[] name) { if (StrEqual(name, "ircrelay")) { ircrelay = false; } } public void OnLibraryAdded(const char[] name) { if (StrEqual(name, "ircrelay")) { ircrelay = true; } }
Creating a Dependency
Allowing other plugins to use your plugin as a library requires making an include file with two structures (the second of which is optional). The first structure must look like this:
public SharedPlugin __pl_myfile = { name = "myfile", file = "myfile.smx", #if defined REQUIRE_PLUGIN required = 1, #else required = 0, #endif };
The basic format is:
- The variable name MUST start with __pl_ and must end with a unique string.
- The "name" portion is treated as the library name and must be unique.
- The filename must match the filename of the plugin implementing the library.
- The requirement portion should remain unchanged in order to maintain standards.
Additionally, you should expose a function which marks all of your natives as optional. You can do this by:
#if !defined REQUIRE_PLUGIN public void __pl_myfile_SetNTVOptional() { MarkNativeAsOptional("native1"); MarkNativeAsOptional("native2"); MarkNativeAsOptional("native3"); } #endif
This function will be secretly called before the plugin loads (if and only if the requirement is optional), thus allowing seamless optional usage by third party developers. Note that the __pl_ and _SetNTVOptional portions must be present, and that everything in between must match the ending of __pl_ for the SharedPlugin structure.
You also must register the library name with SourceMod -- again this should be the unique string. This should be done inside the AskPluginLoad2
function.
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { //... code here ... RegPluginLibrary("myfile"); return APLRes_Success; }
Full Example
- Bounty: bounty.sp
#include <sourcemod> #undef REQUIRE_PLUGIN #include <ircrelay>
- Bounty: bounty.config.sp
bool plugin_IrcRelay = LibraryExists("ircrelay"); if ((BountyIRC) && (plugin_IrcRelay)) { RegisterIrcCommand("!bounty", "x", Irc_ViewBounty); IrcMessage(CHAN_MASTER, "IRC Bounty Running!"); } else { if ((BountyIRC) && (!plugin_IrcRelay)) { BountyConsole_Debug("%t", "Bounty IRC Relay failed"); } }
- IRC Relay: ircrelay.sp
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { //... code here ... RegPluginLibrary("ircrelay"); return APLRes_Success; }
- IRC Relay: ircrelay.inc
public SharedPlugin __pl_ircrelay = { name = "ircrelay", file = "ircrelay.smx", #if defined REQUIRE_PLUGIN required = 1, #else required = 0, #endif }; #if !defined REQUIRE_PLUGIN public void __pl_ircrelay_SetNTVOptional() { MarkNativeAsOptional("IrcMessage"); MarkNativeAsOptional("RegisterIrcCommand"); MarkNativeAsOptional("IrcGetCmdArgc"); MarkNativeAsOptional("IrcGetCmdArgv"); } #endif
The name
value is what will be checked when you run LibraryExists
. This allows the bounty script to fully work, even if the IRC relay plugin is not installed and/or running correctly on your server.