[Tutorial] MySQL ban system using unix timestamps
#1

Good day.
This is a tutorial on how to make a ban system using MySQL and unix timestamps. (Timed)
There may be better ways, but this is how I roll.

NOTE: Please read it carefully before asking questions because this is not a copy paste code tutorial, you need to work your way towards making it. Hence, a tutorial.

Steps:

1) Create an enum for player to store data about his bans in
2) Create a MySQL table to store that data.
3) Retrieve it on the event of the player connecting and allow/disallow entry


1) Creating an enum for players.

pawn Code:
enum pI
{
    pBANNED, // Values 0 or 1 - 1 means banned
    pBANNEDfor[20], // The reason for being banned?
    pBANNEDby[MAX_PLAYER_NAME], // The ADMIN who banned this player
    pBANNEDtime, // the timestamp of being banned. Not necessary but useful for showing stuff.
    pUNBANtime // the time to be unbanned
}
new pINFO[MAX_PLAYERS][pI];
Simple enough. You can learn about enums if you don't know, here.
What we did is create MAX_PLAYERS amount of copies of those variables for each indivisual entity. Something like OOP. (yeah?)

Let's create the table for banning the player now.
Please read about SQL syntax online, here.

pawn Code:
new conn_HANDLE; // Global variable to store connection handle value in.

public OnGameModeInit()
{
    #define SQL_HOST "localhost"
    #define SQL_USER "root"
    #define SQL_PASS ""
    #define SQL_DATABASE "my_database"
    // Defining the specifics for the mysql connection. Usually are the above for a server using localhost wamp server.
   
    conn_HANDLE = mysql_connect(SQL_HOST, SQL_USER, SQL_DATABASE, SQL_PASS);
    // connecting..
    // you can include checks like if(mysql_ping()  > 1) to see if the connection was successful, I am skipping that!
    mysql_function_query(conn_HANDLE, "CREATE TABLE IF NOT EXISTS `playerbans`(username VARCHAR(24), pBANNED INT(1), pBANNEDfor VARCHAR(20), pBANNEDby VARCHAR(24), pBANNEDtime INT(30), pUNBANtime INT(30) )", false, "", "");
    // we create the table, called `playerbans` and create columns like USERNAME of character type, max length 24, pBANNED of just 1 (stores only 0 or 1) and so on.
    // "false" specifies that we don't need to store returned data
    // "" "" mean that we don't need any callback to store returned data (no data!)
    return 1;
}
That's done then, creating the table.

Now we need to insert player NAMES when they register. I ASSUME you have got a register system in place.

pawn Code:
stock YourRegisterSystemStock_WhereYouRegisterAPlayer(playerid)
{
    new name[MAX_PLAYER_NAME];
    GetPlayerName(playerid, name, MAX_PLAYER_NAME);
    // Getting the name of the guy
    new string[128];
    format(string, sizeof(string), "INSERT INTO `playerbans`(username, pBANNED, pBANNEDby, pBANNEDfor, pBANNEDtime, pUNBANtime) VALUES( '%s', 0,'  ','  ', 0, 0)", name);
    // we insert the player's name in the table!
    mysql_function_query(conn_HANDLE, string, false, "", "");
    // Again we don't need to process returned data, so false.
    return 1;
}
Now we are done inserting the player's name in the table!
We can start creating the BAN command now.

pawn Code:
CMD:ban(playerid, params[])
{
    new target_id, hours, minutes, seconds, reason[20], name[MAX_PLAYER_NAME];
    // new variables, purpose stated below.
    if(!IsPlayerAdmin(playerid)) return SendClientMessage(playerid, -1, "SERVER: UNKNOWN COMMAND.");
    // You ain't logged into RCON, dude!
    if(sscanf(params, "uddds[20]", target_id, hours, minutes, seconds, reason)) return SendClientMessage(playerid, -1, "USAGE: /ban (playerid) (hours) (mins) (secs) (reason)");
    // We insert values the command executor typed after ban in the respective variables.
    // u -> player -> target_id - the player to be banned
    // d -> hours -> hours to be banned
    // s -> reason (string) -> 20 max chars.
    // We return him the USAGE string if all parameters were not filled.
    if(!IsPlayerConnected( target_id ) ) return SendClientMessage(playerid, -1, "ERROR: Requested player is offline.");
    {
        // okay, he's admin, he has typed correctly.
        GetPlayerName(playerid, name, MAX_PLAYER_NAME);
        pINFO[target_id][pBANNED] = 1; // okay, now set the ban status of target to 1
        pINFO[target_id][pBANNEDtime] = gettime(); // returns a timestamp of the current moment!
        format( pINFO[target_id][pBANNEDfor], 20, "%s", reason ); // Now pBANNEDfor contains the reason.
        format( pINFO[target_id][pBANNEDby], MAX_PLAYER_NAME, "%s", name); // the person who banned the target
        // the main part. When is he going to be unbanned?
        new total_banned_seconds_from_now = seconds + ( minutes * 60 ) + (hours * 60 * 60);
        // now the variable contains the total seconds the target is to be banned, from now!
        // If you don't know why we multipled by 60, go back to school!
        pINFO[target_id][pUNBANtime] = total_banned_seconds_from_now + gettime();
        // So if I typed /ban player 0 1 50 TEST, current gettime() would be datestamp.
        // let datestamp be 124124. I need to ban 'player' 1 minute and 50 seconds.
        // according to calculations, that is 110 seconds. Hence, the timestamp when the 'player' player is to be unbanned is
        // 124124 + 110 = 124230 !
       
        // Let's update the target_id's table.
       
        new query[200], TARGETname[MAX_PLAYER_NAME];
           
            GetPlayerName(target_id, TARGETname, 24);
       
        format(query, sizeof(query), "UPDATE `playerbans` SET pBANNED = 1, pBANNEDfor = '%s', pBANNEDby  = '%s', pBANNEDtime = %d, pUNBANtime = %d WHERE username = '%s'"  reason, name, gettime(), pINFO[target_id][pUNBANtime], TARGETname);
       
       
        mysql_function_query(conn_HANDLE, query, false, "", "");
       
               
       
        SendClientMessage(target_id, -1, "You have been banned.");
        SendClientMessage(playerid, -1, "You have banned the requested player successfully.");
        // You can format and add effects like gametext, sending to jail, freezing, etc. I am keeping it simple.
        KickWithDelay( target_id );
        // I assume KickWithDelay as the function where you set a timer to kick, etc.
        // The link for it is https://sampwiki.blast.hk/wiki/Kick, look in the example section.
    }
        return 1;
}
Okay. Done with ban command. It should now update the values of the target.
NOTE: Make sure you save them again in OnPlayerDisconnect.

Last step.

OnPlayerConnect, should we allow entry?

pawn Code:
public OnPlayerConnect( playerid )
{
    new query[70], name[24];
    GetPlayerName(playerid, name, 24);
    format(query, 70, "SELECT * FROM `playerbans` WHERE username = '%s'", name); // select ALL from..
    // We select the player row!
    mysql_function_query(conn_HANDLE, query, true, "OnPlayerRowCheck", "d", playerid);
    // We will now check under the callback OnPlayerRowCheck to see if his row exists.
    return 1;
}
Okay now the function.

pawn Code:
forward public OnPlayerRowCheck( playerid );

public OnPlayerRowCheck( playerid )
{
    new rows, fields;
    cache_get_data(rows, fields, dbHANDLE);
    // get amount of rows!
    if(rows)
    {
        // Okay, he has registered and has a row to himself in the mysql database.
        new number[25];
        cache_get_field_content(0, "pBANNEDfor", number), format(pINFO[playerid][bannedFOR], 20, "%s", number);
        cache_get_field_content(0, "pBANNEDby", number), format(pINFO[playerid][bannedBY], 25, "%s", number);
        cache_get_field_content(0, "pUNBANtime", number), pINFO[playerid][unBANTIME] = strval(number);
        cache_get_field_content(0, "pBANNEDtime", number), pINFO[playerid][bannedTIME] = strval(number);
        cache_get_field_content(0, "pBANNED", number), pINFO[playerid][banned] = strval(number);
        // Okay, now we got the data correctly!
       
        CheckForExistingBans( playerid );
    }
    return 1;
}
Now we create the function CheckForExistingBans.

pawn Code:
stock CheckForExistingBans( playerid )
{
    if( pINFO[playerid][banned] == 1 )
    {
        if( gettime() > pINFO[playerid][pUNBANtime] ) // that is, unban time has already gone by, he should be unbanned.
        {
            pINFO[playerid][banned] = 0;
            SendClientMessage(playerid, -1, "You have been unbanned.");
            // reset all other variables to 0 and blank.
        }
        else // he's still banned
        {
            SendClientMessage(playerid, -1, "You are still banned!");
            // Now we tell him HOW much time is remaining for the ban to get over.
            new diff_secs = ( pINFO[playerid][pUNBANtime] - gettime() );
            new remain_hours = floatround( diff_secs / 60 * 60 ); // unbantime - gettime = seconds remaining.
            // dividing by 60 square, we get hours remaining. floatround to be sure.
            new remain_mins = floatround( diff_secs / 60  );
            // doing the same thing, just dividing by 60!
            new remain_secs =  diff_secs % 60;
            // modulo operator gives the remainder after the closest quotient. So if 70 seconds are remaining,
            // 60 + 10 = 70. That means 1 minute and 10 seconds remaining.
            new string[128];
            format(string, 128, "TIME REMAINING TILL UNBAN: %d hours, %d minutes and %d seconds.", remain_hours, remain_mins, remain_secs);
            SendClientMessage(playerid, -1, string);
           
            // Now we kick that player with delay.
            KickWithDelay( playerid );
        }
        return 1;
    }
}
That's it. You can add all other modifications as per your need. These are the basic components you need to get started.

READ ABOUT UNIX TIMESTAMPS HERE.

BUGS:

Please report bugs immediately if you spot one.
I accept that there could be logical as well as syntactical bugs!

Thank you.
Reply
#2

I don't see the need for this:

pawn Code:
stock YourRegisterSystemStock_WhereYouRegisterAPlayer(playerid)
{
    new name[MAX_PLAYER_NAME];
    GetPlayerName(playerid, name, MAX_PLAYER_NAME);
    // Getting the name of the guy
    new string[128];
    format(string, sizeof(string), "INSERT INTO `playerbans`(username, pBANNED, pBANNEDby, pBANNEDfor, pBANNEDtime, pUNBANtime) VALUES( '%s', 0,'  ','  ', 0, 0)", name);
    // we insert the player's name in the table!
    mysql_function_query(conn_HANDLE, string, false, "", "");
    // Again we don't need to process returned data, so false.
    return 1;
}
Why not just add it into the ban table once the player actually gets banned? Using this would mean that if you want to create a PHP ban page, you'd need to set it so if the unix timestamp is 0, then, he'd be not on the list. Which is easy I suppose, but still, I'd just say put that in the ban command.
Reply
#3

You are correct, but I took up this tutorial from my gamemode where I have no separate tables for a ban list and players. Obviously, you can use either ways, so cheers and thanks! This method however allows for crosschecking, better. You add each player. Then when a ban evader joins, you can crosscheck through all players, their IPs, location and previous reasons. This is a feeble advantage which again can be coded the other way, cheers.
Reply
#4

Lovely.

EDIT:

What's wrong with this?
pawn Code:
BanInfo[playerid][pUnbanTime] = gettime()+(hours*60*60);
I banned myself for one hour, and it comes as like 2180hours or something. I can post full BanPlayer if you wish.
Reply
#5

Thanks.
Nothing wrong with the statement. May I see your code?
Reply
#6

pawn Code:
IsPlayerBanned(playerid)
{
    new String[100];
    if(BanInfo[playerid][pBanned] == 1)
    {
        if(gettime() > BanInfo[playerid][pUnbanTime])
        {
            UnbanPlayer(playerid);
            SendClientMessage(playerid, COLOR_WHITE, "You have been unbanned from the server.");
            format(String, sizeof(String), "** %s(ID: %d) has been unbanned from the server.");
            SendClientMessageToAll(COLOR_YELLOW, String);
            return 1;
        }
        else
        {
            new diff_secs = (BanInfo[playerid][pUnbanTime] - gettime());
            new mins = floatround(diff_secs / 60);
            new hours = floatround(diff_secs / 60 * 60);

            new Format[70];
            format(Format, sizeof(Format), "%d Hours %d Minutes", hours, mins);
            format(String, sizeof(String), "You are currently banned. You have %s till the removal of your ban.", Format);
            SendClientMessage(playerid, COLOR_RED, String);
            format(String, sizeof(String), "You were banned for: %s", BanInfo[playerid][pReason]);
            SendClientMessage(playerid, COLOR_RED, String);
            SendClientMessage(playerid, COLOR_RED, "You can appeal this ban at "#serverDomain);
            KickPlayer(playerid, "Banned Account");
            return 1;
        }
    }
    return 1;
}
It works, however it bans the user for 3180 hours.
Reply
#7

I give you a tip, when you save the timestamps as unbantime and the bantime about mysql than get the timestamps about MySQL to instead of the function gettime.
Use the SQL-Command NOW() to return the actual Date and Time and change the colume datatyp from int to timestamp. You can add to this time hours , minutes , days and so on to with DATE_ADD.

A example Query from my own Ban-system

SELECT -Query to load the data
Code:
"SELECT `Admin`, `Grund`, DATE_FORMAT(`Datum`, 'Am %%d.%%m.%%Y  um %%T Uhr') AS `BDatum`, DATE_FORMAT (`EntbanDatum`, '%%d.%%m.%%Y um %%H:%%i:%%s Uhr') AS `BEntbanDatum` ,`IP` FROM `bans` WHERE `Name` = '%e' AND  `EntbanDatum` > NOW() LIMIT 0,1;"
INSERT-Query

PHP Code:
"INSERT INTO `bans` (`Name`, `Admin`, `Grund`, `Datum`, `EntbanDatum`, `IP`, `GPCI`) VALUES ('%s', '%s', '%s', NOW(), DATE_ADD(NOW(), INTERVAL %d DAY), '%s', '%s');" 
to check if the Player is now unbanned or not.
Code:
SELECT `EntbanDatum` FROM `bans` WHERE `EntbanDatum` <= NOW();
EntbanDatum = Unbandate.
Datum = Date
Grund = Reason
Reply
#8

Quote:
Originally Posted by Isolated
View Post
pawn Code:
IsPlayerBanned(playerid)
{
    new String[100];
    if(BanInfo[playerid][pBanned] == 1)
    {
        if(gettime() > BanInfo[playerid][pUnbanTime])
        {
            UnbanPlayer(playerid);
            SendClientMessage(playerid, COLOR_WHITE, "You have been unbanned from the server.");
            format(String, sizeof(String), "** %s(ID: %d) has been unbanned from the server.");
            SendClientMessageToAll(COLOR_YELLOW, String);
            return 1;
        }
        else
        {
            new diff_secs = (BanInfo[playerid][pUnbanTime] - gettime());
            new mins = floatround(diff_secs / 60);
            new hours = floatround(diff_secs / 60 * 60);

            new Format[70];
            format(Format, sizeof(Format), "%d Hours %d Minutes", hours, mins);
            format(String, sizeof(String), "You are currently banned. You have %s till the removal of your ban.", Format);
            SendClientMessage(playerid, COLOR_RED, String);
            format(String, sizeof(String), "You were banned for: %s", BanInfo[playerid][pReason]);
            SendClientMessage(playerid, COLOR_RED, String);
            SendClientMessage(playerid, COLOR_RED, "You can appeal this ban at "#serverDomain);
            KickPlayer(playerid, "Banned Account");
            return 1;
        }
    }
    return 1;
}
It works, however it bans the user for 3180 hours.
Sorry for my English. I'm from Brazil ..

Well, really wrong. If we divide the result by 60 and multiply by 60. back to the value before the split. Attempts to divide by 60 2 times ..

pawn Code:
new hours = floatround(diff_secs / 60 * 60);
Change to:
pawn Code:
new hours = floatround(diff_secs / 60 / 60);
Here it worked that way ...

EDIT: Spellcheck
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)