Before we begin, this article is for anyone of any skill level. If you're just starting out, this will help you avoid nasty habits that you may pick up now or later. If you're an intermediate level scripter, you're probably relatively capable in terms of producing something that people can use, but may have trouble helping others understand your code properly.
This is an addendum to this post, which I don't feel is complete enough and needed some additions.
This article will teach you what I believe are the best coding styles. I don't even follow all of these because I started out with some bad habits and they are hard to break. All of these are tried and tested but may still be up for debate. Some of these are indisputable while others are highly subjective. For those which I think are subjective, I'll always leave multiple options and state that they are not conventions. So without further ado, here we go.
The Opening Comment Block
The best place to start is, logically, the beginning of the plugin, where you place comments explaining what the plugin is and does. I often ignore this section for small plugins or ones which I'm not going to release. This is, however, good form for a plugin which you are planning on releasing on its own. A typical comment block may look like this:
PHP Code:
/*
Copyleft 2008
Plugin thread: http://forums.alliedmods.net/showthread.php?p=633284
DUEL MOD
========
Description
This mod is designed to allow dueling, where players challenge each
other and engage in battle. It is designed for the mod "The Specialists",
but can be used on other mods.
Commands
say /duel - Will challenge the player being looked at to a duel.
say /accept - Will accept a challenge. Note that you can also use /duel,
but you must be looking at the person who challenged you. With /accept,
the last person to challenge you will be accepted.
say /punchingbag - Turns the player into a punching bag
(requires amx_duel_punchingbag to be set to 1).
Credits
Havoc9 Community - Testing help (specifically SaderBiscut and Jimmy Striker).
Lord_Destros - Testing help.
Steely - Testing help.
sawyer - Testing help.
Frost - Testing help.
coderiz - New semiclip method.
Charming - Encouragement.
Changelog:
Jun 1, 2008 - v1.0 - Initial release
Jun 2, 2008 - v1.1 - [FIXED] Some repeated variables
[FIXED] Message printing incorrectly
[FIXED] Duel off not working properly
[ADDED] Punching bag mode
[ADDED] True semiclip
[ADDED] Attack blocking between
duelists <-> players
[ADDED] God mode to normal players
Jun 4, 2008 - v1.2 - [ADDED] Deny command
[ADDED] Pair command
[ADDED] Name parameter to /duel command
[ADDED] Glow cvar
[FIXED] Messages printing incorrectly
*/
- It clearly states what the plugin is and does.
- It clearly states the plugin thread (this is, by far, the most important of all of these).
- It clearly states which game it works for.
- It clearly states commands. Cvars are optional, but I typically leave them in the plugin thread to keep it uncluttered in the source code.
- It clearly states credits. You should always list these both in the source code and in the plugin thread.
- It has a clear and informative changelog so users know which version they're looking at just by glancing at it.
Your opening comment block can look however you want it to, but the most important thing is that it gets the information out. If you find your style is impeding this, it's probably a good idea to revise it.
Macros and the Preprocessor
This section includes stuff such as your header inclusions (a.k.a. "#include") and compiler definitions (a.k.a. "#define"). There's not a whole lot you can do here in terms of messing up convention, but one of the important things to remember is that all macros should be capitalized from beginning to end. For example,
PHP Code:
#define MAX_ITEMS 10
PHP Code:
#define max_items 10
PHP Code:
#define MyNewIsUserAlive(%1) is_user_alive( %1 )
Another stylistic consideration is that your header inclusions should be as minimalistic as possible. For example, if you don't use any FakeMeta functions, don't include the fakemeta.inc file as it is useless clutter.
One other important thing you should know about preprocessor styling is that conditions should generally be left untabbed. This makes it very clear where blocks of code will operate and where they won't. For example:
PHP Code:
PunishUser( id )
{#if defined KILL_USER
user_kill( id )
client_print( 0, print_chat, "[Kill] The user has been killed")#else // KILL_USER
user_slap( id, 0 )
client_print( 0, print_chat, "[Kill] The user has been slapped")#endif // KILL_USER}
The final thing you should know is that, as in my previous example, it's a good idea to leave a comment at each #else, #elseif and #endif command denoting what exactly it's checking.
Indentation, Tabbing and Spacing
Tabbing should always be done whenever you open a code block (which will be discussed more in detail later). A standard tab is either done using the "Tab" key or using 4 spaces. Less commonly, people use 1, 3 or 8 spaces for their tabs, but this is very rare and generally frowned upon. Almost everyone uses the "Tab" key because then other people can set their tab-size higher or lower in their text editors.
There are also many styles of spacing, but I believe the best (despite my not using it personally) is to add a space whenever you put a parameter in brackets, after a comma, immediately after a condition/loop keyword and before/after any operators which take two parameters on the left and right. For example:
PHP Code:
if ( !cmd_access( id, level, cid ) )
PHP Code:
for ( new i; i < 10; i++ )
This is by no means standard and is highly up for debate, but to me this makes the most logical sense and is what I would use if I had the option of immediately wiping my habits and picking a new set of them. I would, however, advise you to always add spaces between operators with two parameters and after commas.
You may also consider adding two carriage returns as opposed to one between functions as it may make them easier to read. This is by no means a convention but I recommend it personally.
Case
This is important mostly for variables and functions. I'm putting this here because I'm going to be implying it in my next section, which covers global variables. This section is largely up for debate, but I believe the strongest and most legible way of doing it is what is commonly referred to as "camel case".
There's a slight variant of camel case called "mixed case" which I believe is slightly better. Here's how it goes:
For functions, you always capitalize the first letter of every word. For example:
PHP Code:
public EventDeathMsg()
PHP Code:
new gMyVariable
new myVariable
new userKills
PHP Code:
new MyVariable
new UserKills
Mixed/camel case more or less necessitates totally avoiding using underscores. For example, this looks really weird:
PHP Code:
new My_Variable
Also, as noted earlier, macros should be full upper-case.
Global Variables and Type Prefixing
Lots of confusion can come from not knowing that something is global. As such, all global variables should be prefixed with some sort of tag identifying them as such. For example:
PHP Code:
new gMyVariable
PHP Code:
new g_MyVariable
You should add a carriage return at the end of every global variable as it makes it easier to search out any variables you need. For example:
PHP Code:
new gMyVariable
new gMyOtherVariable
PHP Code:
new gMyVariable, gMyOtherVariable
- g or g_ - globals (this is perfectly acceptable to use although you should avoid the others)
- p or p_ - pointers (this is also perfectly acceptable to use)
- i - integers/cells
- f or fl - floats
- sz - strings
- b - bools
- h - handles (can also be used in place of the pointer tag since either is technically correct)
- v - vectors (not standard to HN but still useful)
- fn - function (very rarely used even by people who use HN)
PHP Code:
// a global cell
new g_iKills
// a global float
new Float:g_flSpeed
// a bool
new bool:bFlag
// a function
public fnEventResetHUD()
// a pointer
new g_pCvar
In terms of a technical analysis, HN for anything other than global variables and pointers doesn't make any sense. Pawn is typeless and therefore incapable of actual data types. Booleans are actually just a meaningless tag and floats are cells which are converted by the virtual machine (AMXX's C++ side) whenever they are needed. There are many situations where HN will make variable casting/detagging confusing.
Code Blocks
A code block is anything that is separated by curly braces. While this may be an imprecise definition since in many code blocks the curly braces can simply be left out, I'm going to be addressing this style anyway.
The first thing I'd like to address is spacing. There are two common ways of spacing your code blocks, one of which I believe is vastly superior. The first is by adding a return before the condition opening a block, the second is not doing this. For example:
PHP Code:
MyFunction()
{
PHP Code:
MyFunction() {
Another very important concept in code blocks is whether or not to drop them entirely. Under some very special circumstances, you can actually ignore the brackets denoting a code block. There are two cases which make this acceptable:
- There is a single function call or operation inside it, even if it itself requires data from other function calls (i.e. log_amx( "%d", get_user_kills( id ) )).
- There is a single code block inside it, even if it contains a lot more code that does not fit in a single function call.
1.
PHP Code:
MyFunction( id )
user_kill( id )
PHP Code:
MyFunction( id )
if( is_user_alive( id ) )
{
client_print( 0, print_chat, "We are now killing %d", id )
user_kill( id )
}
This method of dropping brackets is highly subjective. There are many arguments both for and against each. Dropping brackets allows you to type less code, but including them makes it less ambiguous. It can be argued, however, that ambiguity is resolved by tabbing even if there's no brackets. This style is up to you and either way is perfectly acceptable.
Commenting
Commenting is one of the most important things you can do for your code. Even if you break all of these habits, you can still get away with it if your commenting is good enough.
Large blocks of text outside of functions should be commented using the "/* ... */" operators, as in the first section which covered the opening code block. All other sections should be commented using the "//" operator. The reason for this is that Pawn does not support nested comments, i.e.:
PHP Code:
/*
this is the first comment
/* this is a nested comment */
*/
If you need nested comments, a little-known way of doing this is by using the preprocessor #if along with the condition 0, i.e.:
PHP Code:
#if 0
this is the first comment#if 0
this is a nested comment#endif
#endif
When commenting, you should keep your comment on each line below 80 characters. This is to prevent the compiler from complaining and is also for people who do not have widescreen displays. The "line below 80 characters" rule applies for any line of code, not just comments. Some people also space their comment out with one extra space (for two spaces in total) after the first line. I believe this makes it more readable, but this is highly subjective.
Your comments should always be as descriptive as possible. You should inform the reader of what you're doing, why and what the result will be. For example, this is a bad comment as it serves no constructive use:
PHP Code:
// Start the for loop.for ( new i; i < 7; i++ )
PHP Code:
// There's a strange memory corruption bug in this function.
// As such, we have to loop through each of the 7 elements
// in the array and set them to the value that they should be.for ( new i; i < 7; i++ )
PHP Code:
//////////////////////////////////
// HELPER FUNCTIONS //
//////////////////////////////////
Whitespace
Whitespace is a very important concept in coding. Although it has technically been covered previously in the "Tabbing and Spacing" section, there is still the issue of when to use carriage returns. If used correctly, whitespace allows people to understand your code more effectively. There are many different styles of using whitespace, but I believe the best is to simply add an extra carriage return whenever you break away from a part of a function which has similar results. For example:
PHP Code:
// Ignore the fact that this function would bear no real
// useful results when actually used in a server.
MyFunction()
{
new id = random_num( 1, 13 )
user_kill( id )
set_pcvar_num( p_MyCvar, 9 )
}
Variable and Function Naming
As a rule of thumb, variable and function names should be as descriptive as possible. For example, this is a bad variable name:
PHP Code:
new temp
PHP Code:
new KillsSinceLastSpawn
For functions, it is generally advised to follow the same rule except in the situation where you're creating a hook. Often, you should tag your hook callbacks with what type of callback they are. For example, an event might be tagged with "Event" or a forward with "Forward". This can be seen as follows:
PHP Code:
register_forward( FM_Spawn, "ForwardSpawn" )
PHP Code:
register_event( "DeathMsg", "EventDeathMsg", "a" )
Extras
This section contains things that didn't really fit into other sections.
For starters, you should always use constants whenever they're provided. For example, use:
PHP Code:
return PLUGIN_CONTINUE
PHP Code:
return 0
Semicolons are useful if you plan on moving to C/C++/PHP or any related language, but are often misused (for example, most people don't know do while loops have to be ended with one) or are passively used. Being a C and other related languages programmer myself, I have no trouble switching between using and not using semicolons. Since it's a convenience Pawn affords, I take it and don't use them. You can force semicolons using the following:
PHP Code:
#pragma semicolon 1
PHP Code:
/**
* Print command activity.
* @param id Calling admin's id
* @param key[] Language key
* @param any:... Arguments to the format function (w/out the id/lang key)
* @return True on success, False on fail
**/
stock UTIL_PrintActivity(const id, const szLangKey[],any:...)
PHP Code:
// Hawk552: Added this function to gather and store player
// names, then sort them alphabetically.
SortNames( Names[][], NumNames, MaxLength )
In conclusion, even if you ignore most of these tips, I hope you follow at least some of them as you will find them invaluable as you move along in the process of learning how to code. Ideally, you should follow all of these, but many people find them difficult to use or not as clean as their alternative.
As I think of more stuff to add, I may do this, but for the most part I believe this tutorial is complete. As always, if you have questions or comments, feel free to post or PM me.
EDIT: I bit the bullet and decided to actually learn to use the style that I advocate here. I have posted a plugin that uses this here:
http://forums.alliedmods.net/showthread.php?t=102839
0 nhận xét:
Post a Comment