y_groups - combining with sscanf custom parameter for best results

Today we are going to study about y_groups. "Ok, so I read that topic, what else there is to know?". I'm going to show you how to use that library in the wild in very commonly occuring situation.

Important note: if you are using Zeex's compiler, you have to add -Z+ flag, otherwise you'll see error about a_samp not being included before sscanf. Sublime build configuration should now look like this: "${project_base_name}.pwn", "-;+", "-v2", "-d3", "-Z+", "-\\)+"

You bore me, let's do something already!

Ok. Let's take a look at our current code. We have two commands: /hello_admins and /hello_admin [id]. We need to check if player we want to greet is logged in, and if he's an admin.

Note: For tutorial's simplicity sake everyone who connects is logged in and an admin, your gamemode probably has some OnPlayerLogin handler.

YCMD:hello_admin(playerid, params[], help)
{
    if (help) {
        return SendClientMessage(playerid, 0xBADA55, "Says hello to speficied admin");
    }

    new
        target = 0
    ;

    if (sscanf(params, "u", target)) return SendClientMessage(playerid, 0xBADA55, "You dun goof'd");
    if (target == INVALID_PLAYER_ID || !Logged[target] || !AdminLevel[target]) return SendClientMessage(playerid, 0xBADA55, "You dun goof'd");

    SendClientMessage(target, 0xBADA55, "Hello mr admin!");
    SendClientMessage(playerid, 0xBADA55, "You said hello to admins!");

    return 1;
}

YCMD:hello_admins(playerid, params[], help)
{
    if (help) {
        return SendClientMessage(playerid, 0xBADA55, "Says hello to all admins");
    }

    foreach (new player: Player) {
        if (!Logged[player] || !AdminLevel[player]) continue; 

        SendClientMessage(player, 0xBADA55, "Hello mr admin!");
    }

    SendClientMessage(playerid, 0xBADA55, "You said hello to admins!");

    return 1;
}
Why you want to touch that, I am using it and it's perfect!

We're talking about how YSI can make your life simpler, remember?
I'll start with showing you our final code, and we'll dig through it step by step.

CreateGroups()
{
    Users = Group_Create("Logged in players");

    for (new i = MAX_ADMIN_LEVEL - 1; i >= 0; --i) {
        Admins[i] = Group_Create(adminRanks[i]);

        if (i == MAX_ADMIN_LEVEL - 1) continue;

        Group_AddChild(Admins[i + 1], Admins[i]);
    }

    //Only logged in users can be admins!
    Group_AddChild(Users, Admins[0]);
}

ToggleDefaultGroups(playerid, bool:mode = true)
{
    Group_SetPlayer(Admins[MAX_ADMIN_LEVEL / 3], playerid, mode);
}

We no longer need Logged array to keep track of logged in users, and Admins group replaces our AdminLevel array. Also as you can see we have cool chain of parent groups

`Users` -> `Admins[0]` -> ... `Admins[4]` 

If someone leaves parent group, he's no longer in descendant groups. If you wanted to create closed admin conference for selected admins, just create a group which is a child of Admins[X], and loop just through those admins. Awesome stuff!

Restricting commands to specific group is themost common usage, but I won't be covering it, as it's already been done. But we'll push automatisation a little further.
Very often you have to check if target player is a member of some specific group. Ok, let's do just that

 if (sscanf(params, "u", target) || target == INVALID_PLAYER_ID) return SendClientMessage(playerid, 0xBADA55, "You dun goof'd");
 if (!Group_GetPlayer(Admins[0], target)) return SendClientMessage(playerid, 0xBADA55, "You dun goof'd");

Neat, we know that target player is logged in and is an admin of rank at least of 1 (inheritance is wonderful!).

You said something about sscanf?

Yup. Are you familiar with "kustom" parameters? What if we could reduce our group checks to minimum? Using that, we kan! (that sucked, sorry)

if (sscanf(params, "k<admin>", target) || target == INVALID_PLAYER_ID) return SendClientMessage(playerid, 0xBADA55, "You dun goof'd");

//(...)

SSCANF:admin(string[])
{
    new
        id = strval(string),
        bool:isAdmin = IsAdmin(id)
    ;

    return isAdmin ? id : INVALID_PLAYER_ID;
}

Wicked, isn't it?

Thanks for reading.