Small scripting tutorials, and how to fix some common errors

For scripting examples for learning how to use AMXX.
User avatar
Archive-Poster
Posts: 1267
Gender:
United States of America

Small scripting tutorials, and how to fix some common errors

Post by Archive-Poster »

Some common errors, and how to fix them.

Error: I get no errors, but I get a 0kb output file, and/or the compiler crashes
Solution: Somewhere in your code, you forgot to close something. This is usually caused by forgetting to put a " at the end of text, but it can also be caused by forgetting a ) or }

Error: warning 202: number of arguments does not match defintion
Solution: On the line it says, you don't have enough paramaters. You forgot to put something in, and the compiler noticed. Just go to that line, compare the command you used to the one in the include file. It might be something as simple as you put a period instead of comma.

Error: error 035: argument type mismatch (argument X)
Solution: One of your arguments is the wrong type. This usually happens when the command is expecting a float, and you gave it an integer (I didn't go over floats here, they can be annoying). Or it could be the command expected a string, and you gave it an integer.

Let's start off with how to get some arguments from the commandline. We will create a basic amx_kick plugin.
Here's the code for it, I will explain it after.

Code: Select all

#include <amxmodx>

public plugin_init() {
    register_plugin("Amx-Kick","0.1","devicenull")
    register_concmd("amx_kick2","do_kick",ADMIN_KICK," user Kicks the specified user")
}
public do_kick(id) {
    if (!(get_user_flags(id)&ADMIN_KICK)) {
        console_print(id,"[AMXX] No access")
        return PLUGIN_HANDLED
    }
    if (read_argc() == 0) {
        console_print(id,"[AMXX] You must specify a user")
        return PLUGIN_HANDLED
    }
    new user[32], uid
    read_argv(1,user,32)
    uid = find_player("bh",user)
    if (uid == 0) {
        console_print(id,"[AMXX] Invalid User Id")
        return PLUGIN_HANDLED
    }
    client_cmd(uid,"echo Kicked from server!")
    client_cmd(uid,"disconnect")
    console_print(id,"Kicked player!")
    return PLUGIN_HANDLED
}

Code: Select all

#include <amxmodx>
The first thing I do is include a file called "amxmodx.inc". This file contains basic commands for working with players, and is included in every plugin. Open this file up, its located in amxx\scriping\include. As you can see, every command in it is documented. There are different includes you can use in addition to this one, if you are working with other modules, like engine or cstrike.

Code: Select all

public plugin_init() {
    register_plugin("Amx-Kick","0.1","devicenull")
    register_concmd("amx_kick2","do_kick",ADMIN_KICK," user Kicks the specified user")
}
In register_plugin, I tell AmxX the name of my plugin, the version, and the author name. This line is in almost every plugin, except for some very very small ones. Its a good idea to put this line in your plugins.
Next, in register_concmd, I tell AmxX that I am creating a new command called "amx_kick2", that it should call "do_kick" when a client uses this command. It also specifys the access level needed (ADMIN_KICK) and the help for the command. You almost always have several of these, and there are different types:
register_srvcmd() - This is used for commands that are only done in the server console (And over RCON)
register_clcmd() - This is used for commands that are only done on clients
register_concmd() - This is used for commands that can be done anywhere

Code: Select all

public do_kick(id) {
This is the code that will be executed for amx_kick2. The variable id is the id of the player that called this command. It is a number from 1-32, representing a slot on the server. This is the first number you see in a "status" display.

Code: Select all

if (!(get_user_flags(id)&ADMIN_KICK)) {
    console_print(id,"[AMXX] No access")
    return PLUGIN_HANDLED
}
This bit of code is used to make sure the player has the correct access. There are a few different ways of doing this, and this is the one that seems simplest to me. I could explain how it works, but its really not that important to know. If the player does not have the correct access, "[AMXX] No access" is printed to their console, and the "return PLUGIN_HANDLED" stops code execution

Code: Select all

if (read_argc() == 0) {
    console_print(id,"[AMXX] You must specify a user")
    return PLUGIN_HANDLED
}
Here we check if they executed the command correctly, it needs to have a user after it (amx_kick2 Username). If it doesn't, we can't kick anyone, because we don't know who to kick. read_argc() returns the number of arguments after the command. If there are no arguments, then it prints "[AMXX] You must specify a user", and we stop code execution.

Code: Select all

new user[32], uid
Here we declare two variables, an array named user, and an integer named uid. Arrays are used to hold strings in small.

Code: Select all

read_argv(1,user,32)
With this command, we get the first argument of the command, and put it into the user variable. The maximum length of the argument that we would get is 32 characters.

Code: Select all

uid = find_player("bh",user)
With this, we take the argument, and try and match it to a username. The "bh" means Ignore bots and find users with the given part of the name.

Code: Select all

if (uid == 0) {
    console_print(id,"[AMXX] Invalid User Id")
    return PLUGIN_HANDLED
}
This makes sure that the user we found was an actual user. It works just like the other checks we did above.

Code: Select all

client_cmd(uid,"echo Kicked from server!")
client_cmd(uid,"disconnect")
Here, we execute two commands on the targets computer. One echos "Kicked from server!" into their console, the other disconnects them from the game. There are many ways to kick clients, this is one of the simpler ones.

Code: Select all

console_print(id,"Kicked player!")
This prints a message to the client that kicked the player, telling them that the player was kicked.

Code: Select all

    return PLUGIN_HANDLED
}
This halts code execution.

A note, the "return PLUGIN_HANDLED" prevents Half life from displaying "Command not found".
User avatar
Archive-Poster
Posts: 1267
Gender:
United States of America

Re: Small scripting tutorials, and how to fix some common errors

Post by Archive-Poster »

NOTE: THIS IS AN OLD VERSION OF SQL, USE SQLX INSTEAD
So, you want to learn how to use SQL in AmxX plugins?
To do this, you need the following things
You need to know the SQL commands, I can't teach you the basics of using it, if you don't understand SQL
You need to know small. I won't be explaining things other then the SQL commands.

Note: This tutorial is going to be using the 0.20 version of SQL, not the 0.16 version. You can get the include file to look at it from the CVS.

And the plugin I will be explaining:
A note: In the interest of clarity, I don't tell the user why the plugin didn't work

Code: Select all

#include <amxmodx>
#include <dbi>
new Sql:dbc
new Result:result
public plugin_init() {
    register_plugin("SQL-Tut","0.1","devicenull")
    register_concmd("sql_insert","sql_insert",ADMIN_KICK," <value> Inserts a value into our table")
    register_concmd("sql_display","sql_display",ADMIN_KICK," Displays values")
    set_task(Float:10.0,"sql_init")
}
public sql_init() {
    new host[64], username[32], password[32], dbname[32], error[32]
    get_cvar_string("amx_sql_host",host,64)
    get_cvar_string("amx_sql_user",username,32)
    get_cvar_string("amx_sql_pass",password,32)
    get_cvar_string("amx_sql_db",dbname,32)
    dbc = dbi_connect(host,username,password,dbname,error,32)
    if (dbc == SQL_FAILED)
        log_amx("[AMXX] SQL Connection Failed")
    else
        dbi_query(dbc,"CREATE TABLE IF NOT EXISTS `tutorial` ( `value` VARCHAR(32) NOT NULL )")
}
public sql_insert(id) {
    if (dbc == SQL_FAILED)  return PLUGIN_HANDLED
    if (!(get_user_flags(id)&ADMIN_KICK))   return PLUGIN_HANDLED
    if (read_argc() == 0) return PLUGIN_HANDLED
    
    new arg[32]
    read_argv(1,arg,32)
    result = dbi_query(dbc,"INSERT INTO tutorial VALUES ( '%s' )",arg)
    if (result == RESULT_OK) dbi_free_result(result)
    if (result == RESULT_FAILED) return PLUGIN_HANDLED
    new dbitype[32]
    dbi_type(dbitype,32)
    console_print(id,"[SQL] Inserted value into %s table succesfully",dbitype)
    return PLUGIN_HANDLED
}
public sql_display(id) {
    if (dbc == SQL_FAILED)  return PLUGIN_HANDLED
    if (!(get_user_flags(id)&ADMIN_KICK))   return PLUGIN_HANDLED
    
    result = dbi_query(dbc,"SELECT * FROM tutorial WHERE 1 LIMIT 5")
    new value[32]
    for (new i=1;i<=dbi_num_rows(result);i++) {
        dbi_nextrow(result)
        dbi_result(result,"value",value[32])
        console_print(id,"Value: %s",value)
    }
    dbi_free_result(result)
    return PLUGIN_HANDLED
}
This really isn't that hard to explain..

Code: Select all

new Sql:dbc
new Result:result
Data base connections are the type Sql, results are type Result

Code: Select all

dbc = dbi_connect(host,username,password,dbname,error,32)
Connects to the DB.. get the proper values out of the cvars.. you can just copy the entire init_sql() sub and use it where ever you want

Code: Select all

if (dbc == SQL_FAILED)
Makes sure we actually connected to the database, you can use dbi_error to get the error message if there is one

Code: Select all

result = dbi_query(dbc,"INSERT INTO tutorial VALUES ( '%s' )",arg)
Does the insertion query, and puts a link to the result in the result variable, you could just do the dbi_query part for insert/delete/update, and not worry about what it returns

Code: Select all

dbi_free_result(result)
You must do this when the returned variable is RESULT_OK, otherwise you get memory leaks, and memory leaks = BAD

Code: Select all

dbi_type(dbitype,32)
Get the type of database you are using.. I really haven't thought of a use for this yet, but its here.

Code: Select all

dbi_num_rows(result)
Returns the number of rows that are in the result.

Code: Select all

dbi_nextrow(result)
Advances to the next row in result.

Code: Select all

dbi_result()
This is kind of annoying. Depending on the datatype expected, you have to use this differently.
Expecting: Use:
Int var = dbi_result(result,"fieldname")
Float dbi_result(result,"fieldname",Float:var)
String dbi_result(result,"fieldname",var,len)

Also, dbi_field() works the same way, with the field number instead of the field name

Code: Select all

dbi_close(dbc)
This would close the sql connection, its not needed unless you create a sql connection and close it in the same sub (Don't do that unless you have to, using one connection for the entire plugin is more efficient). Dbi connections are closed before plugin_end.

You can get most of this from reading the include file, but some of it is a bit annoying to figure out
Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest