[Tutorial] Report System with zcmd and sscanf
#1

So, i am here to show you a simple tutorial about /report, /reports, /reply and /acceptreport command.

So here is the full code and i am gonna show you some little explaination:

https://pastebin.com/CRXdiyG4

Lets start with the includes, so we need to get this two includes. I am using sscanf2 and zcmd.

Variables

This is what we are gonna be using on the script below it.
pawn Code:
#define MAX_REPORTS 1000

#define INVALID_REPORT_ID -1

new JustReported[MAX_PLAYERS];

enum rInfo
{
    rReportFrom[MAX_PLAYER_NAME],
    rReporterID,
    rReport[128],
    rReplyTimer,
    rChecker,
    rBeingUsed
}
new Report[MAX_REPORTS][rInfo];
Functions

On OnGameModeInit, add this code. It works as the heartbeat of the server (1 second).
pawn Code:
public OnGameModeInit()
{
    SetTimer("Heartbeat", 1000, 1);
    return 1;
}

pawn Code:
forward Heartbeat();
public Heartbeat()
{
    for(new i = 0; i < MAX_PLAYERS; i++)
    {
        if(JustReported[i] > 0)
            JustReported--;
    }
    return 1;
}
We need to have a forward first before using public in timers.

pawn Code:
for(new i = 0; i < MAX_PLAYERS; i++)
This will let us loop through all the players online.

pawn Code:
if(JustReported[i] > 0)
        JustReported--;
We need to determine whether the player has submitted a report or not. If yes, we reduce it by 1 every 1 second. So when you /report it will not stay at 25 seconds.

pawn Code:
public OnPlayerConnect(playerid)
{
    JustReported[playerid] = 0;
    return 1;
}
Under OnPlayerConnect, add this to make us able to submit a report.

pawn Code:
public OnPlayerDisconnect(playerid, reason)
{
    for(new i = 0; i < MAX_REPORTS; i++)
    {
        if(Report[i][rReporterID] == playerid && Report[i][rChecker] != INVALID_PLAYER_ID)
        {
            Report[i][rReporterID] = INVALID_PLAYER_ID;
            Report[i][rChecker] = INVALID_PLAYER_ID;
            Report[i][rReporterID] = INVALID_PLAYER_ID;
            format(Report[i][rReport], 128, "None");
            format(Report[i][rReportFrom], 128, "None");
            Report[i][rChecker] = INVALID_PLAYER_ID;
            KillTimer(Report[i][rReplyTimer]);
        }
    }
   
    return 1;
}
Under OnPlayerDisconnect, we determine if the player has still have a report. If he still has, we will remove his report or else we will get a bug when an admin accepts it.

pawn Code:
stock SendReportToAdmin(rid, reportfrom, report[])
{
    Report[rid][rReporterID] = reportfrom;
    new string[128], name[MAX_PLAYER_NAME];
    Report[rid][rBeingUsed] = 1;
    GetPlayerName(reportfrom, name, sizeof(name));
    format(Report[rid][rReport], 128, "%s", report);
    format(Report[rid][rReportFrom], 128, "%s", name);
    for(new i = 0; i < MAX_PLAYERS; i++)
    {
        if(IsPlayerAdmin(i))
        {
            format(string, sizeof(string), "Report from: %s (RID: %d): %s", name, rid, report);
            SendClientMessage(i, -1, string);
        }
    }
}
This will let the report submitted to the admins online. Change the IsPlayerAdmin to your admin level variable.


Commands

pawn Code:
CMD:report(playerid, params[])
{
    new string[128];
    if(!isnull(params))
    {
        if(JustReported[playerid] > 0)
        {
            format(string, sizeof(string), "You already have a report. Please wait %d seconds more.", JustReported[playerid]);
            SendClientMessage(playerid, -1, string);
            return 1;
        }
       
        if(strlen(params) > 50)
            return SendClientMessage(playerid, -1, "Your message is too long - the limit is 50 characters.");
       
       
        new name[MAX_PLAYER_NAME];
        GetPlayerName(playerid, name, sizeof(name));
       
        new reportid;
        for(new i = 0; i < MAX_REPORTS; i++)
        {
            if(Report[i][rBeingUsed] == 0)
            {
                reportid = i;
                break;
            }
        }
       
        JustReported[playerid] = 25;
       
        SendReportToAdmin(reportid, playerid, params);
        SendClientMessage(playerid, -1, "You report has been sent to admins online.");
    }
    else
    {
        SendClientMessage(playerid, -1, "USAGE: /report [reporttext]");
    }
    return 1;
}

CMD:reports(playerid, params[])
{
    if(IsPlayerAdmin(playerid))
    {
        new string[1000];
        SendClientMessage(playerid, -1, "___________________________________________________________");
        SendClientMessage(playerid, -1, "REPORTS:");
        for(new r = 0; r < MAX_REPORTS; r++)
        {
            if(Report[r][rBeingUsed] == 1)
            {
                format(string, sizeof(string), "Report from %s (RID: %d): %s", Report[r][rReportFrom], r, Report[r][rReport]);
            }
        }
        SendClientMessage(playerid, -1, string);
        SendClientMessage(playerid, -1, "___________________________________________________________");
    }
    else
    {
        SendClientMessage(playerid, -1, "You are not an admin.");
    }
    return 1;
}

forward ReplyTimer(reportid);
public ReplyTimer(reportid)
{
    Report[reportid][rReporterID] = INVALID_PLAYER_ID;
    Report[reportid][rChecker] = INVALID_PLAYER_ID;
    format(Report[reportid][rReport], 128, "None");
    format(Report[reportid][rReportFrom], 128, "None");
    Report[reportid][rChecker] = INVALID_PLAYER_ID;
    Report[reportid][rBeingUsed] = 0;
    KillTimer(Report[reportid][rReplyTimer]);
    return 1;
}

CMD:reply(playerid, params[])
{
    new string[128];
    new reportid = INVALID_REPORT_ID;
    for(new i = 0; i < MAX_REPORTS; i++)
    {
        if(Report[i][rReporterID] == playerid && Report[i][rChecker] != INVALID_PLAYER_ID)
        {
            reportid = i;
        }
    }
    if(reportid == INVALID_REPORT_ID)
    {
        SendClientMessage(playerid, -1, "You don't have any reports being reviewed at the moment.");
        return 1;
    }
    if(IsPlayerConnected(Report[reportid][rChecker]))
    {
        new name[MAX_PLAYER_NAME], checkername[MAX_PLAYER_NAME];
        GetPlayerName(playerid, name, sizeof(name));
        GetPlayerName(playerid, checkername, MAX_PLAYER_NAME);
        format(string, sizeof(string), "%s(ID: %d) replies: %s", name, playerid, params);
        SendClientMessage(Report[reportid][rChecker], -1, string);

        format(string, sizeof(string), "Reply sent to %s: %s", checkername, params);
        SendClientMessage(playerid,  -1, string);
    }
    else SendClientMessage(playerid, -1, "Player not connected.");
    return 1;
}

CMD:acceptreport(playerid, params[])
{
    if(IsPlayerAdmin(playerid))
    {
        new string[128], id;
        if(sscanf(params, "d", id)) return SendClientMessage(playerid, -1, "USAGE: /acceptreport [reportid]");
       
        if(Report[id][rBeingUsed] == 0)
        {
            SendClientMessage(playerid, -1, "That report is not being used!");
            return 1;
        }
       
        new playername[MAX_PLAYER_NAME];
        GetPlayerName(playerid, playername, sizeof(playername));
        format(string, sizeof(string), "Admin %s has accepted your report, you have 1 minute to talk to him via /reply", playername);
        SendClientMessage(Report[id][rReporterID], -1, string);
        format(string, sizeof(string), "You had accepted %s's report.", Report[id][rReportFrom]);
        SendClientMessage(playerid, -1, string);
       
        Report[id][rChecker] = playerid;
        Report[id][rBeingUsed] = 0;
        Report[id][rReplyTimer] = SetTimerEx("ReplyTimer", 60000, false, "d", id);
    }
    else
    {
        SendClientMessage(playerid, -1, "You are not an admin.");
    }
    return 1;
}
The commands part will be explained tomorrow-------
Thanks for watching, hope you rep me.
EDIT: If I might have mistakes, please reply it so i can fix it and also sorry for my english. I do not speak english
Reply
#2

Why would you use a timer? I'd prefer to use gettime() as a replacement of it (timer) which is optimized and the most recommended way.
Reply
#3

Quote:
Originally Posted by Logic_
View Post
Why would you use a timer? I'd prefer to use gettime() as a replacement of it (timer) which is optimized and the most recommended way.
C'mon... 500.000 timers will only eat a 20% of CPU
Reply
#4

Make your and other's work easier! Timers are inaccurate and why even use other resources when you already have the enough resources!
Reply
#5

PLS HELP https://imgur.com/a/aXaGs
Reply
#6

Can you post the line which you have error?
Reply
#7

Quote:
Originally Posted by TaiRinsuru
View Post
Can you post the line which you have error?
forward Heartbeat();
public Heartbeat()
{
for(new i = 0; i < MAX_PLAYERS; i++)
{
if(JustReported[i] > 0)
JustReported--;
}
return 1;
}
Reply
#8

Took him 3 months to reply
Reply
#9

Quote:
Originally Posted by FAStingTV
View Post
forward Heartbeat();
public Heartbeat()
{
for(new i = 0; i < MAX_PLAYERS; i++)
{
if(JustReported[i] > 0)
JustReported[i]--; // there
}
return 1;
}
the [i]
Reply


Forum Jump:


Users browsing this thread: 4 Guest(s)