SA-MP Forums

Go Back   SA-MP Forums > SA-MP Scripting and Plugins > Scripting Help > Tutorials

Reply
 
Thread Tools Display Modes
Old 14/04/2015, 07:13 PM   #1
corne
Gangsta
 
corne's Avatar
 
Join Date: Jul 2010
Location: The Netherlands
Posts: 710
Reputation: 202
Default Admin levels with y_groups

Introduction

y_groups is an integral part of many YSI libraries (y_classes, y_commands etc) which provides a single implementation for any grouping of players you may like. Examples of "groups" of players include admin levels, factions, gangs and teams. A collection of people with the same options is a "group", and people can be in multiple "groups" - an admin on a role-play server may still be in a faction for role-playing in-character.

For this article we will develop a very simple script which can add and remove admin commands in one line (so you can make ANY command admin or not).

Librariers and Commands

y_groups can interact with any library with the right interface (making it completely optional). This interface is an article for another time, but y_commands provides this interface (basically it has per-player permissions which can be set and unset by y_groups). First lets create a basic mode with all required libraries and three commands:

Code:
#include <YSI\y_commands>
#include <YSI\y_groups>
#include <sscanf2>

YCMD:kick(playerid, params[], help)
{
	if (help)
	{
		return SendClientMessage(playerid, 0xFF0000AA, "Kicks another player.");
	}
	else
	{
		new
			pid,
			reason[32];
		if (sscanf(params, "uS[32]", pid, reason))
		{
			return SendClientMessage(playerid, 0xFF0000AA, "Parameters: <playerid/name> [reason]");
		}
		if (reason[0])
		{
			SendClientMessage(pid, 0xFF0000AA, "You have been kicked.  Reason:");
			SendClientMessage(pid, 0xFF0000AA, reason);
		}
		else
		{
			SendClientMessage(pid, 0xFF0000AA, "You have been kicked.");
		}
		Kick(pid);
	}
	return 1;
}

YCMD:ban(playerid, params[], help)
{
	if (help)
	{
		return SendClientMessage(playerid, 0xFF0000AA, "Bans another player.");
	}
	else
	{
		new
			pid,
			reason[32];
		if (sscanf(params, "uS[32]", pid, reason))
		{
			return SendClientMessage(playerid, 0xFF0000AA, "Parameters: <playerid/name> [reason]");
		}
		if (reason[0])
		{
			SendClientMessage(pid, 0xFF0000AA, "You have been banned.  Reason:");
			SendClientMessage(pid, 0xFF0000AA, reason);
		}
		else
		{
			SendClientMessage(pid, 0xFF0000AA, "You have been banned.");
		}
		Ban(pid);
	}
	return 1;
}

YCMD:pm(playerid, params[], help)
{
	if (help)
	{
		return SendClientMessage(playerid, 0xFF0000AA, "Sends a private message to another player.");
	}
	else
	{
		new
			pid,
			message[32];
		if (sscanf(params, "us[32]", pid, message))
		{
			return SendClientMessage(playerid, 0xFF0000AA, "Parameters: <playerid/name> <message>");
		}
		SendClientMessage(pid, 0xFF0000AA, "Incoming message:");
		SendClientMessage(pid, 0xFF0000AA, message);
		SendClientMessage(playerid, 0xFF0000AA, "Sent!");
	}
	return 1;
}

main()
{
}
Now those commands are VERY basic, but they're just for show. Currently any player can use those commands, even the "/ban" command - this is not a good idea.

y_groups
y_groups has a very flexible API - it is actually defined in terms of other libraries. In this can the functions available include:
  • Code:
    Group:Group_Create(name[]);
    Creates a new group in to which to place people. The name is currently not optional but will be.
  • Code:
    Group_SetCommand(Group:group, command, bool:set);
    Set whether or not a group can use a command.
  • Code:
    Group_SetGlobalCommand(command, bool:set);
    Set whether or not people can use this command by default.
Those are the main functions. You can replace the word "Command" with any library using y_groups, e.g. "Class" for y_classes.

Permissions

Admin levels using y_groups can be done in two ways. The first is to have every level as a separate group and set all permissions for every level. The second is instead of having groups: "level 1", "level 2" etc, you have groups: "level 1+", "level 2+" etc. With "levels", people in level 2 can use level 1 commands, so they are level 1 admins AND level 2 admins. Lets generically create a number of admin levels:

Code:
// Up to 99.
#define MAX_ADMIN_LEVELS (3)

new
	Group:gAdmins[MAX_ADMIN_LEVELS];

public OnGameModeInit()
{
	new
		name[16];
	for (new i = 0; i != MAX_ADMIN_LEVELS; ++i)
	{
		format(name, sizeof (name), "Admin Level %d+", i + 1);
		gAdmins[i] = Group_Create(name);
	}
}
So we now have three admin levels, we need to set commands to be used only by admins. This is an implementation of the first method of making admin levels (by far the simplest):
Code:
SetAdminCommand(command[], level)
{
	// Get the ID of the command (required):
	new
		id = Command_GetID(command);
	if (level)
	{
		// To be used only by admins.
		// First let no normal people use it.
		Group_SetGlobalCommand(id, false);
		// Then loop through admin levels and set it true on the ones it can be
		// used by and false on the others.
		new
			cl = 0;
		while (cl != MAX_ADMIN_LEVELS)
		{
			new
				Group:group = gAdmins[cl];
			++cl;
			if (cl == level)
			{
				// Set this level as using the command.
				Group_SetCommand(group, id, true);
			}
			else
			{
				// Set this level as not using this command.
				Group_SetCommand(group, id, false);
			}
		}
	}
	else
	{
		// To be used by everyone, so let everyone use it.
		Group_SetGlobalCommand(id, true);
	}
}
You can now load and change admin levels in one place. Updating commands does not require modifying the command in any way - you could even set command permissions in a file or database for dynamic permissions.
Code:
public OnGameModeInit()
{
	// As before.
	new
		name[16];
	for (new i = 0; i != MAX_ADMIN_LEVELS; ++i)
	{
		format(name, sizeof (name), "Admin Level %d+", i + 1);
		gAdmins[i] = Group_Create(name);
	}
	// New.
	// "/kick" is a level 1 admin command.
	SetAdminCommand("kick", 1);
	// "/ban" is a level 2 admin command.
	SetAdminCommand("ban", 2);
	// "/pm" is not an admin command (not required as it's the default).
	SetAdminCommand("pm", 0);
}
In addition, you may have a command which can be used by admins of level 2 AND people in the "police" faction (let's call this command "arrest"):
Code:
new
	Group:gPolice;

public OnGameModeInit()
{
	// As before.
	new
		name[16];
	for (new i = 0; i != MAX_ADMIN_LEVELS; ++i)
	{
		format(name, sizeof (name), "Admin Level %d+", i + 1);
		gAdmins[i] = Group_Create(name);
	}
	SetAdminCommand("kick", 1);
	SetAdminCommand("ban", 2);
	SetAdminCommand("pm", 0);
	// New.
	gPolice = Group_Create("Police faction");
	SetAdminCommand("arrest", 2);
	Group_SetCommand(gPolice, Command_GetID("arrest"), true);
}
Note that "SetAdminCommand" contains a call to "Group_SetGlobalCommand" which prevents people using the command by default. If this was not an admin command but a faction command that call would still be needed.

Players

The one thing missing from the code above is a way to actually add people to a group, fortunately this is easily done with "Group_SetPlayer":
Code:
public OnPlayerConnect(playerid)
{
	if (IsPlayerAdmin(playerid))
	{
		// Add the player to the second admin level.  Default is not a member.
		Group_SetPlayer(gAdmins[2], playerid, true);
		// "false" is used to remove a player.
	}
}
Interestingly, the function "Group_SetPlayer" actually meets part of the requirements of a library to support groups, so you could in theory have groups which are part of other groups - this would be a third way of doing admin commands by simply adding the level 1 admin group to the level 2 admin group.

Other Functions

There are a few other functions available for use. Remember that "Command" can be replaced with "Class" or any other identifier for a library using y_groups:
  • Code:
    Group:Group_Destroy(Group:group);
    Destroys a group. People will loose the permissions afforded to them by this group.
  • Code:
    Group_SetCommandDefault(Group:group, bool:set);
    Normally a new group can not use anything on its own. This function essentially sets the permissions for this group for all commands at once. Note that previously set permissions will be lost.
  • Code:
    Group_SetGlobalCommandDefault(bool:set);
    The global group is everybody on the server. This group has permission to use everything by default, but this default can be changed. If "false" is used it makes jails very easy to create because you don't need to worry about removing the permissions on every command for people in jail - just take them out of all groups.
  • Code:
    bool:Group_GetPlayer(Group:group, playerid);
    Returns whether or not this player is a member of this group.
  • Code:
    Group_SetName(Group:group, name[]);
    Code:
    Group_GetName(Group:group);
    Sets and gets the name of a group.


Iterators
y_groups is foreach compatible:
Code:
foreach (new playerid : Group(gPolice))
{
}

That will loop through all the players in the "gPolice" group.
Code:
foreach (new Group:group : PlayerGroups(playerid))
{
}
That will loop through all the groups that a player is in. Note that foreach was updated a few weeks/months ago to support exactly this because this uses a "Group:" tag.

GROUP_ADD
If you are making a large number of objects (for example), then manually setting the group for every one of them can be tedious. To cover the common case where you want a set of elements in just ONE group (not the global group), there is now a "GROUP_ADD" macro:

PHP Code:
new
    
Group:Group_Create("Admin 1");
GROUP_ADD<g>
{
    
CreateDynamicObject(13370.00.04.00.00.00.0);
    
CreateDynamicObject(13371.00.04.00.00.00.0);
    
CreateDynamicObject(13372.00.04.00.00.00.0);
    
CreateDynamicObject(13373.00.04.00.00.00.0);
    
CreateDynamicObject(13374.00.04.00.00.00.0);
    
CreateDynamicObject(13375.00.04.00.00.00.0);

That will create a line of bins near the centre of the world that ONLY people in group "Admin 1" can see. Note that any objects (or anything else for that matter) created AFTER the "GROUP_ADD" block will have whatever you previously set the default permissions. For reference, the "default default" permissions are that everything is in the "global" group and no other group.

Note that this example used functions from the streamer plugin, which are now supported by y_groups.

Now obviously you can't create commands in this way, because commands already exist in the mode at compile time and are set up automatically by the system. However, you can still "touch" the commands inside the "GROUP_ADD" block to entirely reset their permissions to just the specified group:

PHP Code:
GROUP_ADD(myAdminGroup)
{
    @
YCMD:help;
    @
YCMD:commands;

Using that code, only people in the "myAdminGroup" group will be able to use the commands "help" and "commands". I've frequently tried to think of some way of doing this:

PHP Code:
YCMD:help<gg>(playeridparams[], help)
{

Such that that command only exists for a group called "gg", but as of yet I've not managed it. However, that has given me another idea.

Credits
This post was originally written by Y_Less. I'm merely reposting it for future scripters to develop their knowledge.
__________________
corne is offline   Reply With Quote
Old 02/07/2018, 09:44 AM   #2
Calisthenics
Huge Clucker
 
Join Date: May 2018
Posts: 366
Reputation: 53
Default Re: Admin levels with y_groups

I am aware I bump a 3-year old thread but there are not many threads that document y_groups.

I've decided to use y_commands + y_groups for the administrator team. I'll be using the "levels" method as it is mentioned in this thread but I have got 2 questions.

In SetAdminCommand function, shouldn't it be as the one below?
pawn Code:
if (cl >= level)
If "level" is 1, it should allow any admin with level 1+ to use the command. If it only checks for being equal, it basically ignores the higher-level administrators.

The second question is about setting the administrator to the group. For the case given, there are max 3 admin levels. If someone is set as being level 3, then shouldn't be in all groups (level 1+ group, level 2+ group, level 3 group)?
pawn Code:
for (new i = level_to_be_set - 1; i > -1; i--)
{
    Group_SetPlayer(gAdmins[i], playerid, true);
}
Calisthenics is offline   Reply With Quote
Old 02/07/2018, 01:51 PM   #3
CodeStyle175
Banned
 
Join Date: Apr 2014
Posts: 481
Reputation: 35
Default Re: Admin levels with y_groups

for example i am making roleplay server, why would i want to use y_groups when i could just use simple variable ?
PHP Code:
enum{
GROUP_POLICE
};
if(
User[playerid][Group]!=GROUP_POLICE)return scm(playerid,-1,"You aren't a cop!"); 
CodeStyle175 is offline   Reply With Quote
Old 02/07/2018, 03:53 PM   #4
Y_Less
Beta Tester
 
Y_Less's Avatar
 
Join Date: Jun 2008
Location: 629 - git.io/Y
Posts: 15,132
Reputation: 3170
Default Re: Admin levels with y_groups

A few reasons:

1) y_groups is integrated with most of YSI, so for example you can set classes or commands with group permissions and it will handle the permissions automatically, no need for explicit checks cluttering up your code.

2) Hierarchies - groups can be in other groups, so adding a player to one adds them to all their children, which is exactly how admin levels work.

3) One consistent system. Either have groups; or have admin levels, teams, factions, VIPs, jobs, etc all with separate code. Reducing repetition through abstraction is a good thing.

4) Explicitly using variables like that is just awful coding. Of course that's not really an argument for y_groups explicitly, just good practices - you could fix that simply by putting that check in a function.

5) Speaking of checks, y_groups can check up to 32 permissions in parallel. Got a command only cops, fbi, or level 4+ admins can use? That's three long checks in your code, one in y_groups.

6) Multi script support.

7) Flexibility - good luck updating all your code if you decide people can be in two groups.

8) Already written and tested. This is something way too many people ignore - "but I can write my own". I'm sure you probably can, but why waste the time and effort?
Y_Less is offline   Reply With Quote
Old 02/07/2018, 05:37 PM   #5
CaptainBoi
Huge Clucker
 
Join Date: May 2018
Location: India
Posts: 229
Reputation: 18
Default Re: Admin levels with y_groups

like your tutorial good job
__________________
Code:
Releases
[Tutorial] How to make TDM Team Selection.
[Include] moneybar.inc || Coloured Moneybar || Released
[Tutorial] PAWN Compiler In Notepad++
[FilterScript] Perks System (v: [On Update]) [DINI, ZCMD]
[FilterScript] Realistic Transmission Speedometer
CaptainBoi is offline   Reply With Quote
Old 02/07/2018, 11:33 PM   #6
Mobtiesgangsa
Big Clucker
 
Mobtiesgangsa's Avatar
 
Join Date: Oct 2016
Location: Skopje, Macedonia
Posts: 123
Reputation: 10
Default Re: Admin levels with y_groups

nice tutorial corne love'd it

So far achieved
- y_commands (%44.52)
- y_ini (%32.11)
- y_iterate (%12.5)
__________________


So can i earn money
Mobtiesgangsa is offline   Reply With Quote
Old 03/07/2018, 04:23 AM   #7
Verc
Big Clucker
 
Verc's Avatar
 
Join Date: Apr 2018
Location: 🇯🇵
Posts: 173
Reputation: 29
Default Re: Admin levels with y_groups

Quote:
Originally Posted by Mobtiesgangsa View Post
nice tutorial corne love'd it

So far achieved
- y_commands (%44.52)
- y_ini (%32.11)
- y_iterate (%12.5)
How do you calculate those numbers? Where is it coming from?
Verc is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Admin levels DeoXel Scripting Help 1 13/08/2014 10:24 PM
Admin levels Ejected Server Support 3 23/06/2014 10:31 PM
Admin Levels? DeltaAirlines12 Help Archive 1 30/08/2009 03:57 AM
[UNSOLVED]Admin levels = Different Colors, using V-Admin DEJORDZTA Help Archive 10 29/08/2009 09:52 AM
Admin Levels Kodman262 Help Archive 6 18/02/2009 10:04 PM


All times are GMT. The time now is 05:21 AM.


Powered by vBulletin® Version 3.8.6
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd.