[Tutorial] Admin system for beginners
#1

Howdy,
So a lot of people were requesting this on this forums, on social medias, even on ******* comments, So I thought that here I should step forward and tutor others who aren't aware of such technics to do certain things,
so hope you guys enjoy...

REQUIREMENTS
the last version of sscanf, http://download2124.mediafire.com/h2...00-dneeTHA.rar
ZCMD : https://sampforum.blast.hk/showthread.php?tid=91354
YSI\y_ini : http://forum.sa-mp.com/showthread.ph...957(Pa%C4%8Dio
TUTORIAL
So first of all, you must include the new installed files, they should go beneath the a_samp include,
Код:
#include <a_samp>
#include <ZCMD>
#include <sscanf2>
#include <YSI\y_ini>
now, if we are going to do this, let's make it perfect, we want to make the admin status auto acquired by admins when logging-in, thus we must save the data somewhere, so let's define the path of the data saving,
Код:
#define ADMIN_PATH "/Admins/%s.ini"
Ok, now we want to rename the data-holding file to the player's name, to do so, insert the following code,
Код:
AdminPath(playerid) {
	new
	    str[36],
	    name[MAX_PLAYER_NAME];
	GetPlayerName(playerid, name, sizeof(name));
	format(str, sizeof(str), ADMIN_PATH, name);
	return str;
}
you might have a palm face now, and say, wait..w.wwhat? Wth just happened, well, just chill mate, and let me start explaining what the code above stands for,
so as you can see, we created a string named "str" and set it's size to 36, and the other variable to hold the player's name which is "name" and its size is MAX_PLAYER_NAME, which is by default equal to 24,
after that, we stored the player's name in the variable "name", then comes the "format", which replaces the file's name with the player's name, so that we will final have a .ini file with the player's name,

let's move on.
Now we need to declare new variables, so let's got ahead and use the enum function
Код:
enum E_ADMIN_DATA {
	AdminLevel,
	bool:LoggedIn
}
so we've created the "AdminLevel" variable and the "LoggedIn" boolean, which is by default set to false,
underneath of that, do this:
Код:
new PlayerInfo[MAX_PLAYER_NAME][E_ADMIN_DATA];
OK, now he are done with the first part, make sure that your script look like this :

Код:
#include <a_samp>
#include <ZCMD>
#include <sscanf2>
#include <YSI\y_ini>

#define ADMIN_PATH "/Admins/%s.ini"

AdminPath(playerid) {
	new
	    str[36],
	    name[MAX_PLAYER_NAME];
	GetPlayerName(playerid, name, sizeof(name));
	format(str, sizeof(str), ADMIN_PATH, name);
	return str;
}
enum E_ADMIN_DATA {
	AdminLevel,
	bool:LoggedIn
}
new PlayerInfo[MAX_PLAYER_NAME][E_ADMIN_DATA];
before you do anything, go to "scriptfiles" and create a new folder, thennamed it "Admins"

now, moving the second part, go to the OnPlayerConnect(playerid) callback, and type this:
Код:
PlayerInfo[playerid][AdminLevel] = 0;
PlayerInfo[playerid][LoggedIn] = false;
what the first line means is, when a player their stats would be reseted concerning their AdminLevel until their identify get confirmed, (world perfectly with a login system),
right underneath that, insiter the following,
Код:
	GetPlayerName(playerid, name, sizeof(name));
	if(fexist(AdminPath(playerid))) {
	    INI_ParseFile(AdminPath(playerid), "LoadPlayerData_AdminData", .bExtra = true, .extra = playerid);
	}
so this part should look like this:
Код:
public OnPlayerConnect(playerid)
{
	PlayerInfo[playerid][AdminLevel] = 0;
	PlayerInfo[playerid][LoggedIn] = false;
	new name[MAX_PLAYER_NAME];
	GetPlayerName(playerid, name, sizeof(name));
	if(fexist(AdminPath(playerid))) {
	    INI_ParseFile(AdminPath(playerid), "LoadPlayerData_AdminData", .bExtra = true, .extra = playerid);
	}
	return 1;
}
that part was all about reseting the player's stats and checking if they previousely have an admin data file,
the INI_ParseFile function is calling a forwarded callback, which we are about to creat,
next, go ahead bellow thes OnPlayerConnect callback and forward,
Код:
Код:
forward LoadPlayerData_AdminData(playerid, name[], value[]);
now right below this, type the following code,
Код:
public LoadPlayerData_AdminData(playerid, name[], value[]) {
	INI_Int("AdminLevel", PlayerInfo[playerid][AdminLevel]);
	return 1;
}
that callback above stands for making the admin level data stored in form of an integer, that's why we had in the biggining INI_Int

OK, now we made the necessary part, let's do the setlevel command next,
this is going to work for both level 4 admins and rcon admins, therefore the code should look like this,
Код:
CMD:setlevel(playerid, params[]) {
	if(IsPlayerAdmin(playerid) || PlayerInfo[playerid][AdminLevel] >= 3)
	{
               //do something here
               return 1;
        else {
		SendClientMessage(playerid, 0xFF0000, "[ERROR]: you are not authorized to use this command");
		return 1;
	}
}
next define some variables,
Код:
CMD:setlevel(playerid, params[]) {
	if(IsPlayerAdmin(playerid) || PlayerInfo[playerid][AdminLevel] >= 3)
	{
               new
			string[MAX_PLAYER_NAME+64],
			pname[MAX_PLAYER_NAME],
			tname[MAX_PLAYER_NAME],
			targetid,
		 	level;
               //do something here
               return 1;
        else {
		SendClientMessage(playerid, 0xFF0000, "[ERROR]: you are not authorized to use this command");
		return 1;
	}
}
string[MAX_PLAYER_NAME+64] : a string which is going to be used to display a message, its size is ([MAX_PLAYER_NAME] = 24)+64 which is 88
pname[MAX_PLAYER_NAME] : a variable which will hold the player's name (the admin who is using the command), its size is 24
tname[MAX_PLAYER_NAME] : a variable that holds the target id's name (the player whom is going to be promoted/demoted), its size is 24
targetid : self-explanatory, a variable which helps define the target's id,
level : this will hold the value of the admin level you want to stick to someone,
next step, we are goin to tell the server what to do in case a player uses the command with null parameters, and where comes sscanf's role, so type this in your code

Код:
if(sscanf(params, "ii", targetid, level))
        {
            return SendClientMessage(playerid, -1, "USAGE: /setlevel (playerid) (level)");
        }
then your code should look like this:
Код:
CMD:setlevel(playerid, params[]) {
	if(IsPlayerAdmin(playerid) || PlayerInfo[playerid][AdminLevel] >= 3)
	{
                 new
			string[MAX_PLAYER_NAME+64],
			pname[MAX_PLAYER_NAME],
			tname[MAX_PLAYER_NAME],
			targetid,
		 	level;
                        if(sscanf(params, "ii", targetid, level))
                        {
                                return SendClientMessage(playerid, -1, "USAGE: /setlevel (playerid) (level)");
                        }
                        //do something here
                        return 1;
        else {
		SendClientMessage(playerid, 0xFF0000, "[ERROR]: you are not authorized to use this command");
		return 1;
	}
}
what we've done so far, is sending a client message to the user "USAGE:..etc" in case he didn't add the command params,
so next, we need to check if the targetid(the promoted/demoted guy) is connected or has a valid ID,
in order to do this, we need to loop through all connected players one by one, and the fast way to do this is using "for" statement, so we need to add this,
Код:
for(new i=0;i<MAX_PLAYERS; i++) continue; {

	     if((!IsPlayerConnected(targetid)) || (targetid == INVALID_PLAYER_ID))
            {
                    SendClientMessage(playerid, 0xFF0000, "Player Is Not Connected!");
            }
}
if(level < 1 || level > 4)
{
        return SendClientMessage( playerid, 0xFF0000, "available levels (1-4)");
}
OK, let's freez a minute and explain the content above,
so first we started the code line with for, which stands for a loop, and what loop besically is? a loop is a sequence of instruction s that is continually repeated until a certain condition is reached, in this case, the targetid, so we typed new i = 0, whioch creates a new variable "i" and sets its value to 0, then we typed i<MAX_PLAYERS, which sets the limite of "i" to the max players connected, so now the value of "i"is closed between 0 and MAX_PLAYERS, next we typed, i++, which tells the server to add 1 to the value of "i" every time, then we finished the line with continue, your code will stop and start again at the start of the loop, skipping everything else after the continue;
then we made a limite to admin levels, (1-4),
once you do all of this, your code should look like this:
Код:
CMD:setlevel(playerid, params[])
{
    if((IsPlayerAdmin(playerid)) || PlayerInfo[playerid][AdminLevel] >= 3)
    {
        new
             string[MAX_PLAYER_NAME+64],
             pname[MAX_PLAYER_NAME],
             tname[MAX_PLAYER_NAME],
             targetid,
             level;

	if(sscanf(params, "ii", targetid, level))
        {
            return SendClientMessage(playerid, -1, "USAGE: /setlevel (playerid) (level)");
        }
        for(new i=0;i<MAX_PLAYERS; i++) continue; {
                if((!IsPlayerConnected(targetid)) || (targetid == INVALID_PLAYER_ID))
                {
                        SendClientMessage(playerid, 0xFF0000, "Player Is Not Connected!");
                }
         }
	 if(level < 1 || level > 4)
        {
        	return SendClientMessage( playerid, 0xFF0000, "available levels (1-4)");
	}
    }
    else
    {
         SendClientMessage( playerid, 0xFF0000, "you can't use this command!");
         return 1;
    }
    return 1;
}
and now let's sent the players admin level,
first, let's store the player and target's names in their variables,
Код:
GetPlayerName(playerid, pname, sizeof(pname));
GetPlayerName(targetid, tname, sizeof(tname));
then send a client message to all, so players would notice the promotion,
Код:
format(string, sizeof(string), "Administrator %s has promoted %s to level %i admin", pname, tname, level);
SendClientMessageToAll(-1, string);
now to set the player's level, just type this
Код:
PlayerInfo[targetid][AdminLevel] = level;
we still missing the most important part, which is saving the data, do to so, we're going to use YSI\y_ini,
Код:
new INI:File = INI_Open(AdminPath(targetid));
INI_SetTag(File, "AdminData");
INI_WriteInt(File, "AdminLevel", PlayerInfo[targetid][AdminLevel]);
INI_Close(File);
so far, your overall command code, should look like this,
Код:
CMD:setlevel(playerid, params[])
{
    if((IsPlayerAdmin(playerid)) || PlayerInfo[playerid][AdminLevel] >= 3)
    {
        new
             string[MAX_PLAYER_NAME+64],
             pname[MAX_PLAYER_NAME],
             tname[MAX_PLAYER_NAME],
             targetid,
             level;

		if(sscanf(params, "ii", targetid, level))
        {
            return SendClientMessage(playerid, -1, "USAGE: /setlevel (playerid) (level)");
        }
        for(new i=0;i<MAX_PLAYERS; i++) continue; {
                 if((!IsPlayerConnected(targetid)) || (targetid == INVALID_PLAYER_ID))
                {
                       SendClientMessage(playerid, 0xFF0000, "Player Is Not Connected!");
                }
	}
        if(level < 1 || level > 4)
        {
        	return SendClientMessage( playerid, 0xFF0000, "available levels (1-4)");
        }

        else
        {
            GetPlayerName(playerid, pname, sizeof(pname));
            GetPlayerName(targetid, tname, sizeof(tname));
            format(string, sizeof(string), "Administrator %s has promoted %s to level %i admin", pname, tname, level);
            SendClientMessageToAll(-1, string);
            PlayerInfo[targetid][AdminLevel] = level;
            new INI:File = INI_Open(AdminPath(targetid));
            INI_SetTag(File, "AdminData");
            INI_WriteInt(File, "AdminLevel", PlayerInfo[targetid][AdminLevel]);
            INI_Close(File);
            return 1;
        }
    }
    else
    {
         SendClientMessage( playerid, 0xFF0000, "you can't use this command!");
         return 1;
    }
    return 1;
}
all the data would be saved in "scriptfiles > Admins"
hope I helped most of you guys,
if you find this useful, give it a +REQ
regards, oussama
Reply
#2

Y u do dis?

Код:
for(new i=0;i<MAX_PLAYERS; i++) continue; {
                 if((!IsPlayerConnected(targetid)) || (targetid == INVALID_PLAYER_ID))
                {
                       SendClientMessage(playerid, 0xFF0000, "Player Is Not Connected!");
                }
	}
Reply
#3

Quote:
Originally Posted by Spmn
Посмотреть сообщение
Y u do dis?

Код:
for(new i=0;i<MAX_PLAYERS; i++) continue; {
                 if((!IsPlayerConnected(targetid)) || (targetid == INVALID_PLAYER_ID))
                {
                       SendClientMessage(playerid, 0xFF0000, "Player Is Not Connected!");
                }
	}
when I don't add this, the server doesn't check if the targetid is valid or not,whatever the id you would enter, you would ended up being the targetid instead, even if you do /setlevel 5000 4 it would work
Reply
#4

What about just
Код:
if (targetid == INVALID_PLAYER_ID) return SendClientMessage(playerid, -1, "The specified player is not connected.");
It will do all the job...
Reply
#5

Why are people still using ini? It's so outdated.
Reply
#6

Quote:
Originally Posted by Luis-
Посмотреть сообщение
Why are people still using ini? It's so outdated.
what do you suggest

Quote:
Originally Posted by GoldenLion
Посмотреть сообщение
What about just
Код:
if (targetid == INVALID_PLAYER_ID) return SendClientMessage(playerid, -1, "The specified player is not connected.");
It will do all the job...
I tried it, the command always end up not working as it should, I would enter any ID and the cmd would still work but only on the playerid
Reply
#7

MySQL is much easier than having to mess around with files.
Reply
#8

Quote:
Originally Posted by Luis-
Посмотреть сообщение
MySQL is much easier than having to mess around with files.
can you give me links to some nice tutorials on MySQL? I want to learn it so bad
Reply
#9

Quote:
Originally Posted by Luis-
Посмотреть сообщение
Why are people still using ini? It's so outdated.
Most people start off with Y_ini even though I agree it's outdated but it's good for beginners.Imagine what the 'Script Help' section would be if everyone started off with MySQL xD

Quote:
Originally Posted by Eoussama
Посмотреть сообщение
can you give me links to some nice tutorials on MySQL? I want to learn it so bad
Here you go > https://sampforum.blast.hk/showthread.php?tid=129183

OT: The tutorial is quite useful for beginners.Well done.
Also, use the 'php' tags when making tutorials, it's much better imo.
Reply
#10

OH MY GOD SO MANY WRONG THINGS:

1ST:
Why are you looping to check if the player is connected? if(!IsPlayerConnected(targetid)) !!!!

2ND:
Why are you using 'i' for targetid, use 'u' sscanf supports name/id with 'u'. (IF U DOESNT WORK UPDATE IT)


Quote:
Originally Posted by KeithCooper
View Post
OT: The tutorial is quite useful for beginners.Well done.
WHAT??!!?!?!?! Sure, the INI parts are useful but no beginner should be seeing loops to check if a player is connected. Reminds me of people that don't even download a gm and say "Good job +rep"
Reply
#11

Quote:
Originally Posted by Luis-
View Post
MySQL is much easier than having to mess around with files.
Well MySQL are for pros and low pros, I think that this tutorial is obviously made for beginners who don't know shts about things like MySQL databases.
Reply
#12

@Jamester, you only named two wrong things that he done.Others criticized him above so me doing the same thing would be useless, don't you think?
Reply
#13

Quote:
Originally Posted by KeithCooper
View Post
@Jamester, you only named two wrong things that he done.Others criticized him above so me doing the same thing would be useless, don't you think?
Two MAJOR things. Nope, you spouting useless crap like good job! Sure, parts of it are alright! But the code inside those commands give me the shudders.
Reply
#14

Teaching new programmers terrible ways to do something is terrible on its own. I may sound like a hypocrite since I made a Y_INI tutorial, but I never claimed it was for new programmers. Moreover, I still use .ini for other purposes and not necessarily to excessively read and write data which is not what it should be used for, btw.

And I don't know what your intentions were when you wrote this for-loop:
PHP Code:
for(new i=0;i<MAX_PLAYERSi++) continue; 
If you compile that, it will give you an error saying 'i' is undefined throughout the loop's body, because you're stopping the loop right after 'continue'. It will just loop from 0 to MAX_PLAYERS-1 without doing anything.
Reply
#15

Quote:
Originally Posted by Eoussama
Посмотреть сообщение
what do you suggest


I tried it, the command always end up not working as it should, I would enter any ID and the cmd would still work but only on the playerid
The reason why it's not working is because "i" is an integer specifier, so it never returns INVALID_PLAYER_ID, so you'll have to use IsPlayerConnected(targetid), but if you change it to "u", INVALID_PLAYER_ID is returned, so it should work.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)