[Tutorial] Temporary Ban Commands (MySQL, ZCMD, sscanf)
#1

Temporary Ban Command
I searched for temporary ban command tutorials on the forums, and I struggled to find any. I'm not sure if this is my fault for not using the search tools properly, or if there actually aren't any tutorials, but regardless, I'm going to attempt to teach you how to create a very simple temporary ban system using ZCMD, sscanf and MySQL. By no means is this the most efficient way of creating a banning system, but it works! Also, just a quick note, this tutorial is aimed at people who have at least some understanding of MySQL and how to manage a MySQL database and SA-MP (pawn) coding (& using phpMyAdmin etc.). Anyway, let's get started.

Includes

First of all, we need a few includes, and the plugins that go with them. You can download them all here:

ZCMD
sscanf
MySQL - I'm using R6 for this, as I'm not using threaded queries. If you know how to use threaded queries, by all means do so! (Download link has broken and I cannot find another download... sorry!)

I'm going to assume you know how to where to put the include and plugin files.

Now, we need to include them at the top of our script under the a_samp include:
pawn Код:
#include <a_samp>
#include <sscanf2>
#include <zcmd>
#include <a_mysql>
... and add the plugins to our server.cfg file.
pawn Код:
plugins sscanf mysql
Now, on to the coding.

Coding

First, we need to tell our script to connect to the MySQL database. You may have already done this if you're using a MySQL-based gamemode, so feel free to move on.

I'm going to connect to the database under OnGameModeInit:
pawn Код:
public OnGameModeInit()
{
    mysql_debug(); // I'm enabling the debug so if anything goes wrong, we can check it in the log file.
    mysql_connect("localhost", "root", "tutorial", ""); //Connecting to the database, using mysql_host, mysql_user, mysql_database and mysql_password.
    if(mysql_ping() < 1) //I'm checking the ping of the server to the MySQL server. If this is 0, the connection has failed.
    {
        print("MYSQL CONNECTION FAILED!");
    }
    else
    {
        print("MYSQL CONNECTION SUCCESSFUL!"); //Otherwise, the connection was successful.
    }
By default, your MySQL username should be root and your password shouldn't be set. If you've changed them, you can use your username and password.

Now, we can create the table in the database. We need to go to our MySQL database in phpMyAdmin (or another GUI if you don't use phpMyAdmin) and create a new table called "bans" with 7 columns:


We need to name them as follows:

Make sure to tick the auto increment box on ban_id so each ban has its own unique ID, you'll see why later.

Now, onwards to the command.

I'm going to use ZCMD and sscanf to create my command at the bottom of the gamemode file.

pawn Код:
CMD:tempban(playerid, params[])
{
    new banner[MAX_PLAYER_NAME], banned[MAX_PLAYER_NAME], bannedid, reason[25], hours, string[400], secs, unbants, ip[16], query[180];
Here, I've started to create the command and I've declared some variables. Let's break them down:
  • banner - the name of the administrator who performed the command
  • banned - the name of the player that is being banned
  • bannedid - the ID of the player that is being banned
  • reason - the reason, entered by the admin, for the ban
  • hours - the amount of hours the player will remain banned for
  • string - a string to hold the messages that will be sent to show the ban
  • secs - the hours converted to seconds
  • unbants - the timestamp for the time and date the player will be unbanned
  • ip - the player's ip address
  • query - the mysql query that we'll format
Those will all become clear soon.

Now we need to add some checks that will run when the administrator performs the command:

pawn Код:
if(!IsPlayerAdmin(playerid) && pData[playerid][Level] < 2) return SendClientMessage(playerid, ERROR, "You do not have sufficient permissions to use this command."); //this is just my admin check, yours may be different.
    if(sscanf(params, "uds[25]", bannedid, hours, reason)) return SendClientMessage(playerid, -1, "Usage: /tempban [player name/ID] [hours] [reason]"); // here, we're checking if the administrator entered the command correctly. "u" is a player name or ID, "d" is the amount of hours as an integer, and "s[25]" is the reason.
    if(!IsPlayerConnected(bannedid)) return SendClientMessage(playerid, -1, "Invalid player name/ID."); //checking if the player that the admin is trying to ban is connected to the server
    if(pData[playerid][Level] < pData[bannedid][Level]) return SendClientMessage(playerid, -1, "You cannot ban someone with a higher level than you!"); //I don't allow admins to ban those with higher levels than their own.
    if(strlen(reason) < 3 || strlen(reason) > 24) return SendClientMessage(playerid, -1, "Your reason must be between 3 and 24 characters."); //limiting the reason length to 3-24 characters
    if(hours < 12 || hours > 720) return SendClientMessage(playerid, -1, "Ban hours are between 12 and 720 hours"); //making sure the ban duration is no shorter than 12 hours and no longer than 30 days
Now we've made sure the command is infallible, we can create the main bulk of the command.

We need to save the administrator and the player's names in order to present them in a message later on, as well as the player's IP address, so I'll get it out of the way now:
pawn Код:
GetPlayerName(playerid, banner, sizeof(banner));
    GetPlayerName(bannedid, banned, sizeof(banned));
     GetPlayerIp(bannedid, ip, sizeof(ip));
pawn Код:
secs = hours*3600
Here, I have multiplied the inputted ban hours by the amount of seconds in one hour (3600) to convert the hours to seconds.

Now I can work out the unix timestamp at the time that the player will be unbanned by adding the current unix time to the amount of seconds that the player will be banned for. If you don't know what a unix timestamp is, it is the amount of seconds that have passed since the 1st of January, 1970.
pawn Код:
unbants = gettime()+ secs;
gettime() gets the current unix timestamp.

Now it's time to save our information to the MySQL database. We need to format the "query" variable:
pawn Код:
format(query, sizeof(query), "INSERT INTO `bans` (ban_id, player, ip, admin, reason, ban_time, unban_time) VALUES ('0', '%s', '%s', '%s', '%s', %d, %d)",
     banned, ip, banner, reason, gettime(), unbants);
    mysql_query(query);
Okay, we need to tell the player that they've been banned.
pawn Код:
format(string, sizeof(string), "You've been temporarily banned.\n\n{FFFFFF}Staff member: %s\nReason: %s", banner, reason);
    ShowPlayerDialog(bannedid, 678, DIALOG_STYLE_MSGBOX, "Banned!", string, "Close", "");
I'm showing them by using a dialog. You can present the information in any way you like, of course. Also, you can add more information, for example in my own server, I tell the player the time and date of the ban, and the exact time and date that they'll be able to connect again.

Also, I want to tell everybody else in the server that the person has been banned:
pawn Код:
format(string, sizeof(string), "%s(%d) has banned %s(%d) for %d hours for: %s", banner, playerid, banned, bannedid, hours, reason);
    SendClientMessageToAll(-1, string);
And finally, we need to kick the player. If we kick the player straight away, we won't give the server enough time to send the dialog through to the player, so we need to add a small delay before they're kicked:
pawn Код:
SetTimerEx("KickEx", 500, false, playerid, "i");
I added this timer to the command. This will add a timer of 500 seconds before the player is kicked. Now I can add the function that the timer will run when its time is up. I add this after the command:
pawn Код:
forward KickEx(playerid);
public KickEx(playerid)
{
    Kick(playerid);
    return 1;
}
There. That's the command finished. We're getting close to completing our ban system, but we're missing possibly the most important detail... stopping banned players from entering our server.

Ban Checking
When a player connects to the server, we need to check if their IP address is in our ban table, and if it is, we need to check if their ban has expired or if they need to remain banned.

First, under OnPlayerConnect, we'll declare two variables:
pawn Код:
new banquery[180], ip[16];
Now, we can get the player's IP address.
pawn Код:
GetPlayerIp(playerid, ip, 16);
Great. Now we need to run a query to search the table for an unban time against the player's IP address. This'll let us know if they're in the ban table:
pawn Код:
format(banquery, sizeof(banquery), "SELECT unban_time FROM `bans` WHERE ip = '%s'", ip);
    mysql_query(banquery);
    mysql_store_result();
If an entry was found, we need to check if the ban has expired or not. If it has expired, we let the player connect to the server. If it's still active, we kick the player:
pawn Код:
if(mysql_num_rows() != 0) //if an unban time was found with their ip address
    {
        new unbantimestamp = mysql_fetch_int(); //get the timestamp from the database
        if(unbantimestamp > gettime()) //if the unban time hasn't been reached yet
        {
            SendClientMessage(playerid, -1, "You're banned from this server.");
            SetTimerEx("KickEx", 500, false, playerid, "i");
        }
    }
You can add more of an interesting message there, of course.

You could also, using your knowledge gained from this tutorial, create an unban command which deactivates bans, hence the unique ban IDs.

Well, that's basically it. I don't think I've forgotten anything, but feel free to leave a reply if there's something I didn't explain very well. This is my first tutorial afterall.

Thanks.
Reply


Messages In This Thread

Forum Jump:


Users browsing this thread: 1 Guest(s)