PDA

View Full Version : Writing Sane Plugins


BAILOPAN
06-29-2007, 03:01
It'd be a shame to lose these, so moving this thread here verbatim until someone has time to clean it up to be general advice.

These are rough guidelines for getting your plugin approved.

Write neat code. If your source code is completely unreadable, or not formatted in any noticeable way, it will be ignored or unapproved.
Verify compilation. If your plugin does not compile, it won't be approved.
Check warning. If your plugin has a warning when compiling, it won't be approved. This includes deprecated functions.
Fill in the description and version fields.
If you fill in a version cvar, choose a descriptive cvar name. If your plugin is called "Nifty Tools," a good cvar name would be "niftytools_version." Bad names might be "nt_version," "nt," or "version_n." Check on game-monitor (http://www.game-monitor.com/) that the cvar is not in use for Source. Make sure FCVAR_NOTIFY is applied to the cvar flags.
Don't copycat. If you take someone else's plugin and modify it, you must give them credit for the original work. If your plugin does the same thing as someone else's plugin, it may be unapproved unless it adds new functionality or the original has been abandoned.
Translation files should end in ".txt" and you should not specify the extension in LoadTranslations().
Do not hardcode "addons/sourcemod" -- use BuildPath().
All plugins must have downloads attached. If you want to link to an external site, make sure that the plugin is findable within a reasonable number of clicks. If downloads require registration, it will not be approved.
All plugins must be comply with the SourceMod License, the GNU General Public License version 3. Equivalently licensed source code must be available.
Any "extra" files must comply with the DMCA. Attaching music/sound/video files from other games or authors might cause trouble. Distribute wisely.
Do not use any of our translation files other than core.phrases.txt and common.phrases.txt. The other files can and will change at our discretion, where as common and core will always be backwards compatible.
Do not rely on the existence or contents of any of our gamedata files -- not even core.games.
Do not use strange or proprietary archive formats (.7z, .rar, etc) - use .zip to keep the forums consistent.
Do not place repeated advertising in your plugin. This is generally obnoxious to end users, and creates problems as more and more plugins decide to do it. If you are concerned about a specific case, please PM me or an approver.


Most common mistakes:

You put a public cvar in your code, but not in your post's description section for public cvar.
You create a public cvar without FCVAR_NOTIFY.
Description fields are not filled in at all.
Illegal file formats... no .rar or .7z files.
Compiles on forums but you attach a compiled version. Do not do this if it will compile here.

For those of you who do not know, a public cvar is a console variable that you can create in your code to track your plugin. People usually do this with the version variable. Something like "sm_plugin_version". That is fine, but make sure it is not already taken, you have the proper flags assoicated with it, and you put it in your post's description section.

You are not required to make this, although it makes it work with the stat system and allows you to search for it on game-monitor to see who is using it. If you do make one, you have to follow these guidelines.

A post that has what it takes to be approved can be approved quickly.

You only get one chance at a public cvar. If you change the cvar name, it will not take effect unless your plugin gets unapproved and reapproved. For this, use the "Report Post" button.

If you need the rules clarified, use the "Report Post" button on your plugin thread. (:TODO: post usergroup member list link here)

Antithasys
07-14-2009, 13:15
If you have a post (plugin) in this section please READ THE FIRST POST AGAIN. I am trying to go through all the posts here, started with 6 pages, and now down to 4, after going back up to 5 *sigh*. I am spending half the time telling people to do what is already posted here.

Most common mistakes:

You put a public cvar in your code, but not in your post's description section for public cvar.
You create a public cvar without FCVAR_NOTIFY.
Description fields are not filled in at all.
Illegal file formats... no .rar or .7z files.
Compiles on forums but you attach a compiled version. Do not do this if it will compile here.
For those of you who do not know, a public cvar is a console variable that you can create in your code to track your plugin. People usually do this with the version variable. Something like "sm_plugin_version". That is fine, but make sure it is not already taken, you have the proper flags assoicated with it, and you put it in your post's description section.

You are not required to make this, although it makes it work with the stat system and allows you to search for it on game-monitor to see who is using it. If you do make one, you have to follow these guidelines.

A post that has what it takes to be approved can be approved quickly.

psychonic
03-22-2010, 10:00
Tips to speed up review, or to minimize post-tag between authors and reviewers (based on common flaws/mistakes)
(more guidelines to consider when writing plugins)

Consistency! Consistency! Consistency!
- to elaborate on the "Write Neat Code" guideline above:

Messy code takes much longer to both read and understand than cleanly formatted code. Inconsistent formatting slows things down even more.

Please use only one of each of the following throughout a whole plugin


Tabs/spaces
Block style (all brackets on own line, no brackets on own line, only end brackets on own line)
Variable naming style var_name/varName/iVarName
Padding inside parenthesis (this)/( this )
Space after keyword if() for()/ if () for ()
Semicolons at line end or not

Pick your poison and stick with it.


Make use of the safe defines/constants that you're given
- Sourcemod provides many safe-to-use defines and constants to make your life easier and protect against unexpected issues.

MAXPLAYERS
Max number of players supported across all current versions of the source engine. (useful for defining array size. don't forget to +1 if using client ids as index)

MaxClients
Max number of clients that can currently be in the game. This is updated as map start and will be 0 during OnPluginStart if not a late load. (useful for looping through all clients ingame combined with a IsClientInGame() check).

PLATFORM_MAX_PATH
Max length of a file path. (useful for declaring string size)

MAX_NAME_LENGTH
Max length of a player name. (useful for declaring string size)

On a similar note, when escaping a sql parameter, always using (size*2)+1 to leave room for all possible escape characters.

Use the admin API to your advantage.
Don't hardcode access levels or use flag letters/strings as config values

The CheckCommandAccess and CheckAccess natives allow you to check for access to an existing admin command or even just a named feature. You can still also choose default access flags.

If you hardcode a flag, users would have to edit the source to change it. If you make an admin flag cvar or config value, you bypass the overrides system, not allowing users to 1) utilize the same admin_overrides config to change flag access, and 2) not allowing group overrides at all. Work with the admin API instead of against it.

Do not hardcode offsets or signatures in a plugin

These should be stored in and accessed from gamedata files.

This automatically abstracts the OS (and game/engine where applicable) from SDKCalls and allows for easier updates of this data.


Check your client and entity indexes

Make use of the IsClientInGame, IsClientConnect, IsPlayerAlive, IsValidEdict, and/or IsValidEntity functions where applicable.

Other common checks are that a client index is greater than 0 (not "world") or that players are on a non-spectator team (team index equals 2 or 3 in most games).

This will save you from runtime errors or other unintended effects.


Beware of passing client index to async callbacks

Pass userid rather than client index to asynchronous callbacks (timers, threaded sql calls).

Even with relatively short lengths of time between the calling function and the callback, it is very possible for a client to leave (the client will no longer be "InGame") or worse, a client leave and another client join with the same index, causing you to act upon a player you did not intend to.

SM provides a GetClientSerial function that will be unique per player. Alternatively, userids do not get reused for a long time (after tens of thousands), and it is safe to use GetClientOfUserId on a user that has left (the return will be 0).


Don't write data twice

As a small optimization, you may use "decl" (rather than "new") when creating arrays (including strings and vectors).

This is only safe to do if you are populating the array before accessing it (filling it with values or a string, rather than assuming it will start as 0/blank).

The best places in which to see if this is applicable are


when creating large arrays
in loops with many repetitions
in callbacks that occur often (OnGameFrame, repeated timers with short timing)

(See http://wiki.alliedmods.net/Introduction_to_SourcePawn#decl for more info)

RedSword
01-03-2013, 11:56
Rather than registering (RegConsoleCommand, RegAdminCommand) commands like "say !mycommand" and "say_team !mycommand", simply register "sm_mycommand". This will automatically register commands like sm_mycommand, "say !mycommand", "say_team !mycommand", "say /mycommand" and "say_team /mycommand".

If you're looking to process what is said in the text (i.e. if a user says bad words), you should be using "AddCommandListener".