Converted player groups to YAML (#6488)

Finally adds import functionality for player groups and therefore atcommand and permission defining.

Co-authored-by: Aleos <aleos89@users.noreply.github.com>
This commit is contained in:
Lemongrass3110 2022-04-15 23:47:44 +02:00 committed by GitHub
parent 6e5461a34e
commit 51ddc63a02
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 725 additions and 782 deletions

View File

@ -1,303 +0,0 @@
/*
Player groups configuration file
---------------------------------
This file defines "player groups" and their privileges.
Each group has its id and name, lists of available commands and other
permissions, and a list of other groups it inherits from.
Group settings
--------------
<id>
Unique group number. The only required field.
<name>
Any string. If empty, defaults to "Group <id>". It is used in several @who
commands.
<level>
Equivalent of GM level, which was used in revisions before r15572. You can
set it to any number, but usually it's between 0 (default) and 99. Members of
groups with lower level can not perform some actions/commands (like @kick) on
members of groups with higher level. It is what script command getgmlevel()
returns. Group level can also be used to override trade restrictions
(db/item_trade.txt).
<commands>
A group of settings
<command name> : <bool>
or
<commandname> : [ <bool>, <bool> ]
First boolean value is for atcommand, second one for charcommand. If set to
true, group can use command. If only atcommand value is provided, false is
assumed for charcommand. If a command name is not included, false is assumed for
both atcommand and charcommand.
For a full list of available commands, see: doc/atcommands.txt.
Command names must not be aliases.
<log_commands>
Boolean value. If true then all commands used by the group will be logged to
atcommandlog. If setting is omitted in a group definition, false is assumed.
Requires 'log_commands' to be enabled in 'conf/log_athena.conf'.
<permissions>
A group of settings
<permission> : <bool>
If a permission is not included, false is assumed.
For a full list of available permissions, see: doc/permissions.txt
<inherit>
A list of group names that given group will inherit commands and permissions
from. Group names are case-sensitive.
Inheritance results
-------------------
Both multiple inheritance (Group 2 -> Group 1 and Group 3 -> Group 1) and
recursive inheritance (Group 3 -> Group 2 -> Group 1) are allowed.
Inheritance rules should not create cycles (eg Group 1 inherits from Group 2,
and Group inherits from Group 1 at the same time). Configuration with cycles is
considered faulty and can't be processed fully by server.
Command or permission is inherited ONLY if it's not already defined for the
group.
If group inherits from multiple groups, and the same command or permission is
defined for more than one of these groups, it's undefined which one will be
inherited.
Syntax
------
This config file uses libconfig syntax:
http://www.hyperrealm.com/libconfig/libconfig_manual.html#Configuration-Files
Upgrading from revisions before r15572
-------------------------------------
http://rathena.org/board/index.php?showtopic=58877
*/
groups: (
{
id: 0 /* group 0 is the default group for every new account */
name: "Player"
level: 0
inherit: ( /*empty list*/ )
commands: {
changedress: true
resurrect: true
}
permissions: {
/* without this basic permissions regular players could not
trade or party */
can_trade: true
can_party: true
attendance: true
}
},
{
id: 1
name: "Super Player"
inherit: ( "Player" ) /* can do everything Players can and more */
level: 0
commands: {
/* informational commands */
commands: true
charcommands: true
help: true
rates: true
uptime: true
showdelay: true
exp: true
mobinfo: true
iteminfo: true
whodrops: true
time: true
jailtime: true
hominfo: true
homstats: true
showexp: true
showzeny: true
whereis: true
/* feature commands */
refresh: true
noask: true
noks: true
autoloot: true
alootid: true
autoloottype: true
autotrade: true
request: true
go: true
breakguild: true
channel: true
langtype: true
}
permissions: {
attendance: false
}
},
{
id: 2
name: "Support"
inherit: ( "Super Player" )
level: 1
commands: {
version: true
where: true
jumpto: true
who: true
who2: true
who3: true
whomap: true
whomap2: true
whomap3: true
users: true
broadcast: true
localbroadcast: true
}
log_commands: true
permissions: {
receive_requests: true
view_equipment: true
}
},
{
id: 3
name: "Script Manager"
inherit: ( "Support" )
level: 1
commands: {
tonpc: true
hidenpc: true
shownpc: true
loadnpc: true
unloadnpc: true
npcmove: true
addwarp: true
}
log_commands: true
permissions: {
any_warp: true
}
},
{
id: 4
name: "Event Manager"
inherit: ( "Support" )
level: 1
commands: {
monster: true
monstersmall: true
monsterbig: true
killmonster2: true
cleanarea: true
cleanmap: true
item: [true, true]
zeny: [true, true]
disguise: [true, true]
undisguise: [true, true]
size: [true, true]
raise: true
raisemap: true
day: true
night: true
skillon: true
skilloff: true
pvpon: true
pvpoff: true
gvgon: true
gvgoff: true
allowks: true
me: true
marry: true
divorce: true
refreshall: true
}
log_commands: true
permissions: {
can_trade: false
any_warp: true
}
},
{
id: 5
name: "VIP"
inherit: ( "Player" ) /* can do everything Players can */
level: 0
commands: {
rates: true
who: true
}
permissions: {
/* no permissions by default */
}
},
{
id: 10
name: "Law Enforcement"
inherit: ( "Support" )
level: 2
commands: {
hide: true
follow: true
kick: true
disguise: true
fakename: true
option: true
speed: true
warp: true
kill: true
recall: true
ban: true
block: true
jail: true
jailfor: true
mute: true
storagelist: true
cartlist: true
itemlist: true
stats: true
}
log_commands: true
permissions: {
join_chat: true
kick_chat: true
hide_session: true
who_display_aid: true
hack_info: true
any_warp: true
view_hpmeter: true
}
},
{
id: 99
name: "Admin"
level: 99
inherit: ( "Support", "Law Enforcement" )
commands: {
/* not necessary due to all_commands: true */
}
log_commands: true
permissions: {
can_trade: true
can_party: true
command_enable: true
all_skill: false
all_equipment: false
skill_unconditional: false
use_check: true
use_changemaptype: true
all_commands: true
channel_admin: true
can_trade_bounded: true
item_unconditional: false
bypass_stat_onclone: true
bypass_max_stat: true
/* all_permission: true */
}
}
)

245
conf/groups.yml Normal file
View File

@ -0,0 +1,245 @@
# This file is a part of rAthena.
# Copyright(C) 2022 rAthena Development Team
# https://rathena.org - https://github.com/rathena
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
###########################################################################
# Player Group Database
###########################################################################
#
# Player Group Settings
#
###########################################################################
# - Id Group ID.
# Name Group name.
# Level GM Level used for ranking groups. (Default: 0)
# LogCommands Whether atcommands should be logged or not. (Default: false)
# Commands: List of atcommands that can be used by this group. (Default: none)
# <atcommand name> Whether the specified atcommand can be used by this group or not.
# CharCommands: List of charcommands that can be used by this group. (Default: none)
# <charcommand name> Whether the specified charcommand can be used by this group or not.
# Permissions: List of permissions the group has. (Default: none)
# <permission name> Whether the group has this permission or not.
# Inherit: List of groups that will be inherited. (Default: none)
# <group name> Whether this group will be inherited or not.
###########################################################################
Header:
Type: PLAYER_GROUP_DB
Version: 1
Body:
- Id: 0
# group 0 is the default group for every new account
Name: Player
Level: 0
Commands:
changedress: true
resurrect: true
Permissions:
# without this basic permissions regular players could not trade or party
can_trade: true
can_party: true
attendance: true
- Id: 1
Name: Super Player
# Can do everything Players can and more
Inherit:
Player: true
Level: 0
Commands:
# Informational commands
commands: true
charcommands: true
help: true
rates: true
uptime: true
showdelay: true
exp: true
mobinfo: true
iteminfo: true
whodrops: true
time: true
jailtime: true
hominfo: true
homstats: true
showexp: true
showzeny: true
whereis: true
# Feature commands
refresh: true
noask: true
noks: true
autoloot: true
alootid: true
autoloottype: true
autotrade: true
request: true
go: true
breakguild: true
channel: true
langtype: true
Permissions:
attendance: false
- Id: 2
Name: Support
Inherit:
Super Player: true
Level: 1
Commands:
version: true
where: true
jumpto: true
who: true
who2: true
who3: true
whomap: true
whomap2: true
whomap3: true
users: true
broadcast: true
localbroadcast: true
LogCommands: true
Permissions:
receive_requests: true
view_equipment: true
- Id: 3
Name: Script Manager
Inherit:
Support: true
Level: 1
Commands:
tonpc: true
hidenpc: true
shownpc: true
loadnpc: true
unloadnpc: true
npcmove: true
addwarp: true
LogCommands: true
Permissions:
any_warp: true
- Id: 4
Name: Event Manager
Inherit:
Support: true
Level: 1
Commands:
monster: true
monstersmall: true
monsterbig: true
killmonster2: true
cleanarea: true
cleanmap: true
item: true
zeny: true
disguise: true
undisguise: true
size: true
raise: true
raisemap: true
day: true
night: true
skillon: true
skilloff: true
pvpon: true
pvpoff: true
gvgon: true
gvgoff: true
allowks: true
me: true
marry: true
divorce: true
refreshall: true
CharCommands:
item: true
zeny: true
disguise: true
undisguise: true
size: true
LogCommands: true
Permissions:
can_trade: false
any_warp: true
- Id: 5
Name: VIP
# Can do everything Players can
Inherit:
Player: true
Level: 0
Commands:
rates: true
who: true
- Id: 10
Name: Law Enforcement
Inherit:
Support: true
Level: 2
Commands:
hide: true
follow: true
kick: true
disguise: true
fakename: true
option: true
speed: true
warp: true
kill: true
recall: true
ban: true
block: true
jail: true
jailfor: true
mute: true
storagelist: true
cartlist: true
itemlist: true
stats: true
LogCommands: true
Permissions:
join_chat: true
kick_chat: true
hide_session: true
who_display_aid: true
hack_info: true
any_warp: true
view_hpmeter: true
- Id: 99
Name: Admin
Level: 99
Inherit:
Support: true
Law Enforcement: true
LogCommands: true
Permissions:
can_trade: true
can_party: true
command_enable: true
all_skill: false
all_equipment: false
skill_unconditional: false
use_check: true
use_changemaptype: true
all_commands: true
channel_admin: true
can_trade_bounded: true
item_unconditional: false
bypass_stat_onclone: true
bypass_max_stat: true
#all_permission: true
Footer:
Imports:
- Path: conf/import/groups.yml

View File

@ -0,0 +1,41 @@
# This file is a part of rAthena.
# Copyright(C) 2022 rAthena Development Team
# https://rathena.org - https://github.com/rathena
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
###########################################################################
# Player Group Database
###########################################################################
#
# Player Group Settings
#
###########################################################################
# - Id Group ID.
# Name Group name.
# Level GM Level used for ranking groups. (Default: 0)
# LogCommands Whether atcommands should be logged or not. (Default: false)
# Commands: List of atcommands that can be used by this group. (Default: none)
# <atcommand name> Whether the specified atcommand can be used by this group or not.
# CharCommands: List of charcommands that can be used by this group. (Default: none)
# <charcommand name> Whether the specified charcommand can be used by this group or not.
# Permissions: List of permissions the group has. (Default: none)
# <permission name> Whether the group has this permission or not.
# Inherit: List of groups that will be inherited. (Default: none)
# <group name> Whether this group will be inherited or not.
###########################################################################
Header:
Type: PLAYER_GROUP_DB
Version: 1

View File

@ -616,7 +616,7 @@ ACMD_FUNC(mapmove)
if (!map_search_freecell(NULL, m, &x, &y, 10, 10, 1))
x = y = 0; //Invalid cell, use random spot.
}
if ((map_getmapflag(m, MF_NOWARPTO) && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) || !pc_job_can_entermap((enum e_job)sd->status.class_, m, sd->group_level)) {
if ((map_getmapflag(m, MF_NOWARPTO) && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) || !pc_job_can_entermap((enum e_job)sd->status.class_, m, pc_get_group_level(sd))) {
clif_displaymessage(fd, msg_txt(sd,247)); // You are not authorized to warp to this map.
return -1;
}
@ -794,7 +794,7 @@ ACMD_FUNC(who) {
case 2: {
StringBuf_Printf(&buf, msg_txt(sd,343), pl_sd->status.name); // "Name: %s "
if (pc_get_group_id(pl_sd) > 0) // Player title, if exists
StringBuf_Printf(&buf, msg_txt(sd,344), pc_group_id2name(pc_get_group_id(pl_sd))); // "(%s) "
StringBuf_Printf(&buf, msg_txt(sd,344),pl_sd->group->name.c_str()); // "(%s) "
StringBuf_Printf(&buf, msg_txt(sd,347), pl_sd->status.base_level, pl_sd->status.job_level,
job_name(pl_sd->status.class_)); // "| Lv:%d/%d | Job: %s"
break;
@ -804,7 +804,7 @@ ACMD_FUNC(who) {
StringBuf_Printf(&buf, msg_txt(sd,912), pl_sd->status.char_id, pl_sd->status.account_id); // "(CID:%d/AID:%d) "
StringBuf_Printf(&buf, msg_txt(sd,343), pl_sd->status.name); // "Name: %s "
if (pc_get_group_id(pl_sd) > 0) // Player title, if exists
StringBuf_Printf(&buf, msg_txt(sd,344), pc_group_id2name(pc_get_group_id(pl_sd))); // "(%s) "
StringBuf_Printf(&buf, msg_txt(sd,344), pl_sd->group->name.c_str()); // "(%s) "
StringBuf_Printf(&buf, msg_txt(sd,348), mapindex_id2name(pl_sd->mapindex), pl_sd->bl.x, pl_sd->bl.y); // "| Location: %s %d %d"
break;
}
@ -814,7 +814,7 @@ ACMD_FUNC(who) {
StringBuf_Printf(&buf, msg_txt(sd,343), pl_sd->status.name); // "Name: %s "
if (pc_get_group_id(pl_sd) > 0) // Player title, if exists
StringBuf_Printf(&buf, msg_txt(sd,344), pc_group_id2name(pc_get_group_id(pl_sd))); // "(%s) "
StringBuf_Printf(&buf, msg_txt(sd,344), pl_sd->group->name.c_str()); // "(%s) "
if (p != NULL)
StringBuf_Printf(&buf, msg_txt(sd,345), p->party.name); // " | Party: '%s'"
if (g != NULL)
@ -7284,7 +7284,7 @@ ACMD_FUNC(adjgroup)
return -1;
}
if (!pc_group_exists(new_group)) {
if (!player_group_db.exists(new_group)) {
clif_displaymessage(fd, msg_txt(sd,1227)); // Specified group does not exist.
return -1;
}
@ -9552,11 +9552,11 @@ static void atcommand_commands_sub(struct map_session_data* sd, const int fd, At
switch( type ) {
case COMMAND_CHARCOMMAND:
if( cmd->char_groups[sd->group_pos] == 0 )
if( cmd->char_groups[sd->group->index] == 0 )
continue;
break;
case COMMAND_ATCOMMAND:
if( cmd->at_groups[sd->group_pos] == 0 )
if( cmd->at_groups[sd->group->index] == 0 )
continue;
break;
default:
@ -9815,17 +9815,17 @@ ACMD_FUNC(addperm) {
return -1;
}
if( add && (sd->permissions&pc_g_permission_name[i].permission) ) {
if( add && pc_has_permission( sd, pc_g_permission_name[i].permission) ){
sprintf(atcmd_output, msg_txt(sd,1381),sd->status.name,pc_g_permission_name[i].name); // User '%s' already possesses the '%s' permission.
clif_displaymessage(fd, atcmd_output);
return -1;
} else if ( !add && !(sd->permissions&pc_g_permission_name[i].permission) ) {
}else if( !add && !pc_has_permission( sd, pc_g_permission_name[i].permission ) ){
sprintf(atcmd_output, msg_txt(sd,1382),sd->status.name,pc_g_permission_name[i].name); // User '%s' doesn't possess the '%s' permission.
clif_displaymessage(fd, atcmd_output);
sprintf(atcmd_output,msg_txt(sd,1383),sd->status.name); // -- User '%s' Permissions
clif_displaymessage(fd, atcmd_output);
for( i = 0; i < perm_size; i++ ) {
if( sd->permissions&pc_g_permission_name[i].permission ) {
if( pc_has_permission( sd, pc_g_permission_name[i].permission ) ){
sprintf(atcmd_output,"- %s",pc_g_permission_name[i].name);
clif_displaymessage(fd, atcmd_output);
}
@ -9834,10 +9834,11 @@ ACMD_FUNC(addperm) {
return -1;
}
if( add )
sd->permissions |= pc_g_permission_name[i].permission;
else
sd->permissions &=~ pc_g_permission_name[i].permission;
if( add ){
sd->permissions.set( pc_g_permission_name[i].permission );
}else{
sd->permissions.reset( pc_g_permission_name[i].permission );
}
sprintf(atcmd_output, msg_txt(sd,1384),sd->status.name); // User '%s' permissions updated successfully. The changes are temporary.
@ -11212,7 +11213,7 @@ bool is_atcommand(const int fd, struct map_session_data* sd, const char* message
return false; // No command found. Display as normal message.
info = get_atcommandinfo_byname(atcommand_alias_db.checkAlias(command + 1));
if (!info || info->char_groups[sd->group_pos] == 0) // If we can't use or doesn't exist: don't even display the command failed message
if (!info || info->char_groups[sd->group->index] == 0) // If we can't use or doesn't exist: don't even display the command failed message
return false;
}
@ -11295,8 +11296,8 @@ bool is_atcommand(const int fd, struct map_session_data* sd, const char* message
// type == 1 : player invoked
if (type == 1) {
if ((is_atcommand && info->at_groups[sd->group_pos] == 0) ||
(!is_atcommand && info->char_groups[sd->group_pos] == 0) )
if ((is_atcommand && info->at_groups[sd->group->index] == 0) ||
(!is_atcommand && info->char_groups[sd->group->index] == 0) )
return false;
if( pc_isdead(sd) && pc_has_permission(sd,PC_PERM_DISABLE_CMD_DEAD) ) {
@ -11319,29 +11320,22 @@ bool is_atcommand(const int fd, struct map_session_data* sd, const char* message
return true;
}
void atcommand_db_load_groups(int* group_ids) {
void atcommand_db_load_groups(){
DBIterator *iter = db_iterator(atcommand_db);
AtCommandInfo* cmd;
int i;
int pc_group_max = player_group_db.size();
for (cmd = (AtCommandInfo*)dbi_first(iter); dbi_exists(iter); cmd = (AtCommandInfo*)dbi_next(iter)) {
cmd->at_groups = (char*)aMalloc( pc_group_max * sizeof(char) );
cmd->char_groups = (char*)aMalloc( pc_group_max * sizeof(char) );
for(i = 0; i < pc_group_max; i++) {
if( pc_group_can_use_command(group_ids[i], cmd->command, COMMAND_ATCOMMAND ) )
cmd->at_groups[i] = 1;
else
cmd->at_groups[i] = 0;
if( pc_group_can_use_command(group_ids[i], cmd->command, COMMAND_CHARCOMMAND ) )
cmd->char_groups[i] = 1;
else
cmd->char_groups[i] = 0;
for( auto& it : player_group_db ){
cmd->at_groups[it.second->index] = it.second->can_use_command( cmd->command, COMMAND_ATCOMMAND );
cmd->char_groups[it.second->index] = it.second->can_use_command( cmd->command, COMMAND_CHARCOMMAND );
}
}
dbi_destroy(iter);
return;
}
void atcommand_db_clear(void) {

View File

@ -25,7 +25,7 @@ bool is_atcommand(const int fd, struct map_session_data* sd, const char* message
void do_init_atcommand(void);
void do_final_atcommand(void);
void atcommand_db_load_groups(int* group_ids);
void atcommand_db_load_groups();
bool atcommand_exists(const char* name);

View File

@ -1342,7 +1342,7 @@ int intif_parse_WisEnd(int fd)
*/
static int mapif_parse_WisToGM_sub(struct map_session_data* sd,va_list va)
{
int permission = va_arg(va, int);
e_pc_permission permission = va_arg(va, e_pc_permission);
char *wisp_name;
char *message;
int len;

View File

@ -294,6 +294,7 @@
<Copy SourceFiles="$(SolutionDir)conf\import-tmpl\atcommands.yml" DestinationFolder="$(SolutionDir)conf\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)conf\import\atcommands.yml')" />
<Copy SourceFiles="$(SolutionDir)conf\import-tmpl\battle_conf.txt" DestinationFolder="$(SolutionDir)conf\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)conf\import\battle_conf.txt')" />
<Copy SourceFiles="$(SolutionDir)conf\import-tmpl\char_conf.txt" DestinationFolder="$(SolutionDir)conf\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)conf\import\char_conf.txt')" />
<Copy SourceFiles="$(SolutionDir)conf\import-tmpl\groups.yml" DestinationFolder="$(SolutionDir)conf\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)conf\import\groups.yml')" />
<Copy SourceFiles="$(SolutionDir)conf\import-tmpl\inter_conf.txt" DestinationFolder="$(SolutionDir)conf\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)conf\import\inter_conf.txt')" />
<Copy SourceFiles="$(SolutionDir)conf\import-tmpl\inter_server.yml" DestinationFolder="$(SolutionDir)conf\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)conf\import\inter_server.yml')" />
<Copy SourceFiles="$(SolutionDir)conf\import-tmpl\log_conf.txt" DestinationFolder="$(SolutionDir)conf\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)conf\import\log_conf.txt')" />

View File

@ -1858,7 +1858,7 @@ int npc_touch_areanpc(struct map_session_data* sd, int16 m, int16 x, int16 y, st
case NPCTYPE_WARP:
if ((!nd->trigger_on_hidden && (pc_ishiding(sd) || (sd->sc.count && sd->sc.data[SC_CAMOUFLAGE]))) || pc_isdead(sd))
break; // hidden or dead chars cannot use warps
if (!pc_job_can_entermap((enum e_job)sd->status.class_, map_mapindex2mapid(nd->u.warp.mapindex), sd->group_level))
if (!pc_job_can_entermap((enum e_job)sd->status.class_, map_mapindex2mapid(nd->u.warp.mapindex), pc_get_group_level(sd)))
break;
if (sd->count_rewarp > 10) {
ShowWarning("Prevented infinite warp loop for player (%d:%d). Please fix NPC: '%s', path: '%s'\n", sd->status.account_id, sd->status.char_id, nd->exname, nd->path);

View File

@ -443,7 +443,7 @@ int pc_get_group_id(struct map_session_data *sd) {
* @return Group Level
*/
int pc_get_group_level(struct map_session_data *sd) {
return sd->group_level;
return sd->group->level;
}
/**
@ -12454,9 +12454,12 @@ bool pc_isautolooting(struct map_session_data *sd, t_itemid nameid)
* @param command Command name without @/# and params
* @param type is it atcommand or charcommand
*/
bool pc_can_use_command(struct map_session_data *sd, const char *command, AtCommandType type)
{
return pc_group_can_use_command(pc_get_group_id(sd), command, type);
bool pc_can_use_command( struct map_session_data *sd, const char *command, AtCommandType type ){
return sd->group->can_use_command( command, type );
}
bool pc_has_permission( struct map_session_data* sd, e_pc_permission permission ){
return sd->permissions.test( permission );
}
/**
@ -12464,9 +12467,8 @@ bool pc_can_use_command(struct map_session_data *sd, const char *command, AtComm
* according to their group setting.
* @param sd Player map session data
*/
bool pc_should_log_commands(struct map_session_data *sd)
{
return pc_group_should_log_commands(pc_get_group_id(sd));
bool pc_should_log_commands( struct map_session_data *sd ){
return sd->group->log_commands;
}
/**

View File

@ -4,6 +4,7 @@
#ifndef PC_HPP
#define PC_HPP
#include <bitset>
#include <memory>
#include <vector>
@ -19,6 +20,7 @@
#include "itemdb.hpp" // MAX_ITEMGROUP
#include "map.hpp" // RC_ALL
#include "mob.hpp" //e_size
#include "pc_groups.hpp" // s_player_group
#include "script.hpp" // struct script_reg, struct script_regstr
#include "searchstore.hpp" // struct s_search_store_info
#include "status.hpp" // unit_data
@ -408,8 +410,9 @@ struct map_session_data {
} special_state;
uint32 login_id1, login_id2;
uint64 class_; //This is the internal job ID used by the map server to simplify comparisons/queries/etc. [Skotlex]
int group_id, group_pos, group_level;
unsigned int permissions;/* group permissions */
int group_id;
std::shared_ptr<s_player_group> group;
std::bitset<PC_PERM_MAX> permissions; // group permissions have to be copied, because they might be adjusted by atcommand addperm
int count_rewarp; //count how many time we being rewarped
int langtype;
@ -1238,7 +1241,7 @@ bool pc_can_give_bounded_items(struct map_session_data *sd);
bool pc_can_trade_item(map_session_data *sd, int index);
bool pc_can_use_command(struct map_session_data *sd, const char *command, AtCommandType type);
#define pc_has_permission(sd, permission) ( ((sd)->permissions&permission) != 0 )
bool pc_has_permission( struct map_session_data* sd, e_pc_permission permission );
bool pc_should_log_commands(struct map_session_data *sd);
void pc_setrestartvalue(struct map_session_data *sd, char type);

View File

@ -3,353 +3,343 @@
#include "pc_groups.hpp"
#include "../common/conf.hpp"
#include "../common/db.hpp"
#include "../common/malloc.hpp"
#include "../common/showmsg.hpp"
#include "../common/socket.hpp"
#include "../common/strlib.hpp" // strcmp
#include "../common/socket.hpp" // set_eof
#include "../common/strlib.hpp" // strcmpi
#include "../common/utilities.hpp"
#include "atcommand.hpp" // AtCommandType
#include "pc.hpp" // e_pc_permission
#include "pc.hpp" // struct map_session_data
typedef struct GroupSettings GroupSettings;
using namespace rathena;
// Cached config settings/pointers for quick lookup
struct GroupSettings {
unsigned int id; // groups.[].id
int level; // groups.[].level
const char *name; // groups.[].name
config_setting_t *commands; // groups.[].commands
unsigned int e_permissions; // packed groups.[].permissions
bool log_commands; // groups.[].log_commands
/// Following are used only during config reading
config_setting_t *permissions; // groups.[].permissions
config_setting_t *inherit; // groups.[].inherit
bool inheritance_done; // have all inheritance rules been evaluated?
config_setting_t *root; // groups.[]
int group_pos;/* pos on load */
};
int pc_group_max; /* known number of groups */
static config_t pc_group_config;
static DBMap* pc_group_db; // id -> GroupSettings
static DBMap* pc_groupname_db; // name -> GroupSettings
/**
* @retval NULL if not found
* @private
*/
static inline GroupSettings* id2group(int group_id)
{
return (GroupSettings*)idb_get(pc_group_db, group_id);
const std::string PlayerGroupDatabase::getDefaultLocation() {
return std::string( conf_path ) + "/groups.yml";
}
/**
* @retval NULL if not found
* @private
*/
static inline GroupSettings* name2group(const char* group_name)
{
return (GroupSettings*)strdb_get(pc_groupname_db, group_name);
bool PlayerGroupDatabase::parseCommands( const ryml::NodeRef& node, std::vector<std::string>& commands ){
for( const auto& it : node.children() ){
std::string command;
c4::from_chars( it.key(), &command );
bool allowed;
if( !this->asBool( node, command, allowed ) ){
return false;
}
/**
* Loads group configuration from config file into memory.
* @private
*/
static void read_config(void)
{
config_setting_t *groups = NULL;
const char *config_filename = "conf/groups.conf"; // FIXME hardcoded name
int group_count = 0;
if (conf_read_file(&pc_group_config, config_filename))
return;
groups = config_lookup(&pc_group_config, "groups");
if (groups != NULL) {
GroupSettings *group_settings = NULL;
DBIterator *iter = NULL;
int i, loop = 0;
group_count = config_setting_length(groups);
for (i = 0; i < group_count; ++i) {
int id = 0, level = 0;
const char *groupname = NULL;
int log_commands = 0;
config_setting_t *group = config_setting_get_elem(groups, i);
if (!config_setting_lookup_int(group, "id", &id)) {
ShowConfigWarning(group, "pc_groups:read_config: \"groups\" list member #%d has undefined id, removing...", i);
config_setting_remove_elem(groups, i);
--i;
--group_count;
continue;
if( !atcommand_exists( command.c_str() ) ){
this->invalidWarning( it, "Unknown atcommand: %s\n", command.c_str() );
return false;
}
if (id2group(id) != NULL) {
ShowConfigWarning(group, "pc_groups:read_config: duplicate group id %d, removing...", i);
config_setting_remove_elem(groups, i);
--i;
--group_count;
continue;
util::tolower( command );
if( allowed ){
if( util::vector_exists( commands, command ) ){
this->invalidWarning( it, "Group already has command \"%s\". Please check your data.\n", command.c_str() );
return false;
}else{
commands.push_back( command );
}
config_setting_lookup_int(group, "level", &level);
config_setting_lookup_bool(group, "log_commands", &log_commands);
if (!config_setting_lookup_string(group, "name", &groupname)) {
char temp[20];
config_setting_t *name = NULL;
snprintf(temp, sizeof(temp), "Group %d", id);
if ((name = config_setting_add(group, "name", CONFIG_TYPE_STRING)) == NULL ||
!config_setting_set_string(name, temp)) {
ShowError("pc_groups:read_config: failed to set missing group name, id=%d, skipping... (%s:%d)\n",
id, config_setting_source_file(group), config_setting_source_line(group));
continue;
}
config_setting_lookup_string(group, "name", &groupname); // Retrieve the pointer
}
if (name2group(groupname) != NULL) {
ShowConfigWarning(group, "pc_groups:read_config: duplicate group name %s, removing...", groupname);
config_setting_remove_elem(groups, i);
--i;
--group_count;
continue;
}
CREATE(group_settings, GroupSettings, 1);
group_settings->id = id;
group_settings->level = level;
group_settings->name = groupname;
group_settings->log_commands = log_commands != 0;
group_settings->inherit = config_setting_get_member(group, "inherit");
group_settings->commands = config_setting_get_member(group, "commands");
group_settings->permissions = config_setting_get_member(group, "permissions");
group_settings->inheritance_done = false;
group_settings->root = group;
group_settings->group_pos = i;
strdb_put(pc_groupname_db, groupname, group_settings);
idb_put(pc_group_db, id, group_settings);
}
group_count = config_setting_length(groups); // Save number of groups
// Check if all commands and permissions exist
iter = db_iterator(pc_group_db);
for (group_settings = (GroupSettings *)dbi_first(iter); dbi_exists(iter); group_settings = (GroupSettings *)dbi_next(iter)) {
config_setting_t *commands = group_settings->commands, *permissions = group_settings->permissions;
int count = 0, j;
// Make sure there is "commands" group
if (commands == NULL)
commands = group_settings->commands = config_setting_add(group_settings->root, "commands", CONFIG_TYPE_GROUP);
count = config_setting_length(commands);
for (j = 0; j < count; ++j) {
config_setting_t *command = config_setting_get_elem(commands, j);
const char *name = config_setting_name(command);
if (!atcommand_exists(name)) {
ShowConfigWarning(command, "pc_groups:read_config: non-existent command name '%s', removing...", name);
config_setting_remove(commands, name);
--j;
--count;
}
}
// Make sure there is "permissions" group
if (permissions == NULL)
permissions = group_settings->permissions = config_setting_add(group_settings->root, "permissions", CONFIG_TYPE_GROUP);
count = config_setting_length(permissions);
for(j = 0; j < count; ++j) {
config_setting_t *permission = config_setting_get_elem(permissions, j);
const char *name = config_setting_name(permission);
int p;
ARR_FIND(0, ARRAYLENGTH(pc_g_permission_name), p, strcmp(pc_g_permission_name[p].name, name) == 0);
if (p == ARRAYLENGTH(pc_g_permission_name)) {
ShowConfigWarning(permission, "pc_groups:read_config: non-existent permission name '%s', removing...", name);
config_setting_remove(permissions, name);
--p;
--count;
}else{
if( !util::vector_exists( commands, command ) ){
this->invalidWarning( it, "Group does not have command \"%s\". Please check your data.\n", command.c_str() );
return false;
}else{
util::vector_erase_if_exists( commands, command );
}
}
}
dbi_destroy(iter);
// Apply inheritance
i = 0; // counter for processed groups
while (i < group_count) {
iter = db_iterator(pc_group_db);
for (group_settings = (GroupSettings *)dbi_first(iter); dbi_exists(iter); group_settings = (GroupSettings *)dbi_next(iter)) {
config_setting_t *inherit = NULL,
*commands = group_settings->commands,
*permissions = group_settings->permissions;
int j, inherit_count = 0, done = 0;
if (group_settings->inheritance_done) // group already processed
continue;
if ((inherit = group_settings->inherit) == NULL ||
(inherit_count = config_setting_length(inherit)) <= 0) { // this group does not inherit from others
++i;
group_settings->inheritance_done = true;
continue;
return true;
}
for (j = 0; j < inherit_count; ++j) {
GroupSettings *inherited_group = NULL;
const char *groupname = config_setting_get_string_elem(inherit, j);
uint64 PlayerGroupDatabase::parseBodyNode( const ryml::NodeRef& node ){
uint32 groupId;
if (groupname == NULL) {
ShowConfigWarning(inherit, "pc_groups:read_config: \"inherit\" array member #%d is not a name, removing...", j);
config_setting_remove_elem(inherit,j);
continue;
}
if ((inherited_group = name2group(groupname)) == NULL) {
ShowConfigWarning(inherit, "pc_groups:read_config: non-existent group name \"%s\", removing...", groupname);
config_setting_remove_elem(inherit,j);
continue;
}
if (!inherited_group->inheritance_done)
continue; // we need to do that group first
// Copy settings (commands/permissions) that are not defined yet
if (inherited_group->commands != NULL) {
int l = 0, commands_count = config_setting_length(inherited_group->commands);
for (l = 0; l < commands_count; ++l)
config_setting_copy(commands, config_setting_get_elem(inherited_group->commands, l));
if( !this->asUInt32( node, "Id", groupId ) ){
return 0;
}
if (inherited_group->permissions != NULL) {
int l = 0, permissions_count = config_setting_length(inherited_group->permissions);
for (l = 0; l < permissions_count; ++l)
config_setting_copy(permissions, config_setting_get_elem(inherited_group->permissions, l));
std::shared_ptr<s_player_group> group = this->find( groupId );
bool exists = group != nullptr;
if( !exists ){
if( !this->nodesExist( node, { "Name", "Level" })) {
return 0;
}
++done; // copied commands and permissions from one of inherited groups
group = std::make_shared<s_player_group>();
group->id = groupId;
group->permissions.reset();
}
if (done == inherit_count) { // copied commands from all of inherited groups
++i;
group_settings->inheritance_done = true; // we're done with this group
}
}
dbi_destroy(iter);
if( this->nodeExists( node, "Name" ) ){
std::string name;
if (++loop > group_count) {
ShowWarning("pc_groups:read_config: Could not process inheritance rules, check your config '%s' for cycles...\n",
config_filename);
if( !this->asString( node, "Name", name ) ){
return 0;
}
group->name = name;
}
if( this->nodeExists( node, "Level" ) ){
uint32 level;
if( !this->asUInt32( node, "Level", level ) ){
return 0;
}
if (level > 99) {
this->invalidWarning(node["Level"], "Group level %u exceeds 99, capping.\n", level);
level = 99;
}
group->level = level;
}
if( this->nodeExists( node, "LogCommands" ) ){
bool log;
if( !this->asBool( node, "LogCommands", log ) ){
return 0;
}
group->log_commands = log;
}else{
if( !exists ){
group->log_commands = false;
}
}
if( this->nodeExists( node, "Commands" ) && !this->parseCommands( node["Commands"], group->commands ) ){
return 0;
}
if( this->nodeExists( node, "CharCommands" ) && !this->parseCommands( node["CharCommands"], group->char_commands ) ){
return 0;
}
if( this->nodeExists( node, "Permissions" ) ){
const auto& permissions = node["Permissions"];
for( const auto& it : permissions ){
std::string permission;
c4::from_chars( it.key(), &permission );
bool allowed;
if( !this->asBool( permissions, permission, allowed ) ){
return 0;
}
const char* str = permission.c_str();
bool found = false;
for( const auto& permission_name : pc_g_permission_name ){
if( strcmpi( "all_permission", str ) == 0 ){
if( allowed ){
group->permissions.set();
}else{
group->permissions.reset();
}
found = true;
break;
}else if( strcmpi( permission_name.name, str ) == 0 ){
if( allowed ){
group->permissions.set( permission_name.permission );
}else{
group->permissions.reset( permission_name.permission );
}
found = true;
break;
}
} // while(i < group_count)
}
// Pack permissions into GroupSettings.e_permissions for faster checking
iter = db_iterator(pc_group_db);
for (group_settings = (GroupSettings *)dbi_first(iter); dbi_exists(iter); group_settings = (GroupSettings *)dbi_next(iter)) {
config_setting_t *permissions = group_settings->permissions;
int c, count = config_setting_length(permissions);
if( !found ){
this->invalidWarning( it, "Unknown permission: %s\n", str );
return 0;
}
}
}
for (c = 0; c < count; ++c) {
config_setting_t *perm = config_setting_get_elem(permissions, c);
const char *name = config_setting_name(perm);
int val = config_setting_get_bool(perm);
int j;
if( this->nodeExists( node, "Inherit" ) ){
const auto& inherits = node["Inherit"];
if (val == 0) // does not have this permission
auto& inheritanceVector = this->inheritance[groupId];
for( const auto& it : inherits ){
std::string inherit;
c4::from_chars( it.key(), &inherit );
bool enable;
if( !this->asBool( inherits, inherit, enable ) ){
return 0;
}
util::tolower( inherit );
if( enable ){
if( !util::vector_exists( inheritanceVector, inherit ) ){
inheritanceVector.push_back( inherit );
}
}else{
if( !util::vector_exists( inheritanceVector, inherit ) ){
this->invalidWarning( it, "Trying to remove inheritance of non-inherited group %s\n", inherit.c_str() );
return 0;
}
util::vector_erase_if_exists( inheritanceVector, inherit );
}
}
}
if( !exists ){
this->put( groupId, group );
}
return 1;
}
void PlayerGroupDatabase::loadingFinished(){
static const int MAX_CYCLES = 10;
int i;
for( i = 0; i < MAX_CYCLES; i++ ){
auto inheritanceIt = this->inheritance.begin();
while( inheritanceIt != this->inheritance.end() ){
auto& entry = *inheritanceIt;
if( entry.second.empty() ){
inheritanceIt = this->inheritance.erase( inheritanceIt );
continue;
ARR_FIND(0, ARRAYLENGTH(pc_g_permission_name), j, strcmp(pc_g_permission_name[j].name, name) == 0);
group_settings->e_permissions |= pc_g_permission_name[j].permission;
}
}
dbi_destroy(iter);
}
ShowStatus("Done reading '" CL_WHITE "%d" CL_RESET "' groups in '" CL_WHITE "%s" CL_RESET "'.\n", group_count, config_filename);
std::shared_ptr<s_player_group> group = this->find( entry.first );
auto it = entry.second.begin();
if( ( pc_group_max = group_count ) ) {
DBIterator *iter = db_iterator(pc_group_db);
GroupSettings *group_settings = NULL;
int* group_ids = (int*)aMalloc( pc_group_max * sizeof(int) );
int i = 0;
for (group_settings = (GroupSettings *)dbi_first(iter); dbi_exists(iter); group_settings = (GroupSettings *)dbi_next(iter)) {
group_ids[i++] = group_settings->id;
while( it != entry.second.end() ){
std::string& otherName = *it;
bool found = false;
bool inherited = false;
for( const auto& it : *this ){
std::shared_ptr<s_player_group> otherGroup = it.second;
// Copy the string
std::string otherGroupName = otherGroup->name;
util::tolower( otherGroupName );
if( otherName == otherGroupName ){
found = true;
auto* otherGroupInheritance = util::map_find( this->inheritance, otherGroup->id );
if( otherGroupInheritance != nullptr && !otherGroupInheritance->empty() ){
// Try it again in the next cycle
break;
}
atcommand_db_load_groups(group_ids);
aFree(group_ids);
dbi_destroy(iter);
// Inherit atcommands
for( auto& command : otherGroup->commands ){
if( !util::vector_exists( group->commands, command ) ){
group->commands.push_back( command );
}
}
/**
* Removes group configuration from memory.
* @private
*/
static void destroy_config(void)
{
config_destroy(&pc_group_config);
// Inherit charcommands
for( auto& command : otherGroup->char_commands ){
if( !util::vector_exists( group->char_commands, command ) ){
group->char_commands.push_back( command );
}
}
/**
* In group configuration file, setting for each command is either
* <commandname> : <bool> (only atcommand), or
* <commandname> : [ <bool>, <bool> ] ([ atcommand, charcommand ])
* Maps AtCommandType enums to indexes of <commandname> value array,
* COMMAND_ATCOMMAND (1) being index 0, COMMAND_CHARCOMMAND (2) being index 1.
* @private
*/
static inline int AtCommandType2idx(AtCommandType type) { return (type-1); }
// Inherit permissions
group->permissions |= otherGroup->permissions;
inherited = true;
break;
}
}
if( inherited ){
it = entry.second.erase( it );
continue;
}else if( !found ){
ShowError( "Inherited group \"%s\" for group id %u does not exist.\n", otherName.c_str(), group->id );
it = entry.second.erase( it );
continue;
}else{
it++;
}
}
}
if( this->inheritance.empty() ){
break;
}
}
if( i == MAX_CYCLES && !this->inheritance.empty() ){
ShowError( "Could not process inheritance rules, check your config for cycles...\n" );
}
// Not needed anymore
this->inheritance.clear();
uint32 index = 0;
for( auto& it : *this ){
it.second->index = index++;
}
// Initialize command cache
atcommand_db_load_groups();
}
PlayerGroupDatabase player_group_db;
/**
* Checks if player group can use @/#command
* @param group_id ID of the group
* @param command Command name without @/# and params
* @param type enum AtCommanndType { COMMAND_ATCOMMAND = 1, COMMAND_CHARCOMMAND = 2 }
*/
bool pc_group_can_use_command(int group_id, const char *command, AtCommandType type)
{
int result = 0;
config_setting_t *commands = NULL;
GroupSettings *group = NULL;
if (pc_group_has_permission(group_id, PC_PERM_USE_ALL_COMMANDS))
bool s_player_group::can_use_command( const std::string& command, AtCommandType type ){
if( this->has_permission( PC_PERM_USE_ALL_COMMANDS ) ){
return true;
if ((group = id2group(group_id)) == NULL)
return false;
commands = group->commands;
if (commands != NULL) {
config_setting_t *cmd = NULL;
// <commandname> : <bool> (only atcommand)
if (type == COMMAND_ATCOMMAND && config_setting_lookup_bool(commands, command, &result))
return result != 0;
// <commandname> : [ <bool>, <bool> ] ([ atcommand, charcommand ])
if ((cmd = config_setting_get_member(commands, command)) != NULL &&
config_setting_is_aggregate(cmd) && config_setting_length(cmd) == 2)
return config_setting_get_bool_elem(cmd, AtCommandType2idx(type)) != 0;
}
std::string command_lower = command;
util::tolower( command_lower );
switch( type ){
case COMMAND_ATCOMMAND:
return util::vector_exists( this->commands, command_lower );
case COMMAND_CHARCOMMAND:
return util::vector_exists( this->char_commands, command_lower );
}
return false;
}
/**
* Load permission for player based on group id
* @param sd Player
*/
void pc_group_pc_load(struct map_session_data * sd) {
GroupSettings *group = NULL;
if ((group = id2group(sd->group_id)) == NULL) {
std::shared_ptr<s_player_group> group = player_group_db.find( sd->group_id );
if( group == nullptr ){
ShowWarning("pc_group_pc_load: %s (AID:%d) logged in with unknown group id (%d)! kicking...\n",
sd->status.name,
sd->status.account_id,
@ -357,108 +347,50 @@ void pc_group_pc_load(struct map_session_data * sd) {
set_eof(sd->fd);
return;
}
sd->permissions = group->e_permissions;
sd->group_pos = group->group_pos;
sd->group_level = group->level;
sd->group = group;
sd->permissions = group->permissions;
}
/**
* Checks if player group has a permission
* @param group_id ID of the group
* @param permission permission to check
*/
bool pc_group_has_permission(int group_id, int permission)
{
GroupSettings *group = NULL;
return (group = id2group(group_id)) == NULL ? false : (group->e_permissions&permission) != 0;
bool s_player_group::has_permission( e_pc_permission permission ){
return this->permissions.test( permission );
}
/**
* Checks commands used by player group should be logged
* @param group_id ID of the group
*/
bool pc_group_should_log_commands(int group_id)
{
GroupSettings *group = NULL;
return (group = id2group(group_id)) == NULL ? false : group->log_commands;
bool s_player_group::should_log_commands(){
return this->log_commands;
}
/**
* Checks if player group with given ID exists.
* @param group_id group id
* @returns true if group exists, false otherwise
* Initialize PC Groups and read config.
*/
bool pc_group_exists(int group_id)
{
return idb_exists(pc_group_db, group_id);
void do_init_pc_groups( void ){
player_group_db.load();
}
/**
* Group ID -> group name lookup. Used only in @who atcommands.
* @param group_id group id
* @return group name
* @public
* Finalize PC Groups
*/
const char* pc_group_id2name(int group_id)
{
GroupSettings *group = id2group(group_id);
if (group == NULL)
return "Non-existent group!";
return group->name;
}
/**
* Group ID -> group level lookup. A way to provide backward compatibility with GM level system.
* @param group id
* @return group level
* @public
*/
int pc_group_id2level(int group_id)
{
GroupSettings *group = id2group(group_id);
if (group == NULL)
return 0;
return group->level;
}
/**
* Initialize PC Groups: allocate DBMaps and read config.
* @public
*/
void do_init_pc_groups(void)
{
pc_group_db = idb_alloc(DB_OPT_RELEASE_DATA);
pc_groupname_db = stridb_alloc(DB_OPT_DUP_KEY, 0);
read_config();
}
/**
* Finalize PC Groups: free DBMaps and config.
* @public
*/
void do_final_pc_groups(void)
{
if (pc_group_db != NULL)
db_destroy(pc_group_db);
if (pc_groupname_db != NULL )
db_destroy(pc_groupname_db);
destroy_config();
void do_final_pc_groups( void ){
player_group_db.clear();
}
/**
* Reload PC Groups
* Used in @reloadatcommand
* @public
*/
void pc_groups_reload( void ){
struct map_session_data* sd = NULL;
struct s_mapiterator* iter;
do_final_pc_groups();
do_init_pc_groups();
player_group_db.reload();
/* refresh online users permissions */
iter = mapit_getallusers();
for (sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter)) {
struct s_mapiterator* iter = mapit_getallusers();
for( struct map_session_data* sd = (struct map_session_data*)mapit_first(iter); mapit_exists(iter); sd = (struct map_session_data*)mapit_next(iter) ){
pc_group_pc_load(sd);
}
mapit_free(iter);

View File

@ -4,62 +4,58 @@
#ifndef PC_GROUPS_HPP
#define PC_GROUPS_HPP
#include "../common/cbasetypes.hpp"
#include <bitset>
#include <map>
#include <vector>
#include "../common/cbasetypes.hpp"
#include "../common/database.hpp"
// Forward declaration from atcommands.hpp
enum AtCommandType : uint8;
extern int pc_group_max;
bool pc_group_exists(int group_id);
bool pc_group_can_use_command(int group_id, const char *command, AtCommandType type);
bool pc_group_has_permission(int group_id, int permission);
bool pc_group_should_log_commands(int group_id);
const char* pc_group_id2name(int group_id);
int pc_group_id2level(int group_id);
void pc_group_pc_load(struct map_session_data *sd);
void do_init_pc_groups(void);
void do_final_pc_groups(void);
void pc_groups_reload(void);
enum e_pc_permission : uint32 {
PC_PERM_NONE = 0,
PC_PERM_TRADE = 0x00000001,
PC_PERM_PARTY = 0x00000002,
PC_PERM_ALL_SKILL = 0x00000004,
PC_PERM_USE_ALL_EQUIPMENT = 0x00000008,
PC_PERM_SKILL_UNCONDITIONAL = 0x00000010,
PC_PERM_JOIN_ALL_CHAT = 0x00000020,
PC_PERM_NO_CHAT_KICK = 0x00000040,
PC_PERM_HIDE_SESSION = 0x00000080,
PC_PERM_WHO_DISPLAY_AID = 0x00000100,
PC_PERM_RECEIVE_HACK_INFO = 0x00000200,
PC_PERM_WARP_ANYWHERE = 0x00000400,
PC_PERM_VIEW_HPMETER = 0x00000800,
PC_PERM_VIEW_EQUIPMENT = 0x00001000,
PC_PERM_USE_CHECK = 0x00002000,
PC_PERM_USE_CHANGEMAPTYPE = 0x00004000,
PC_PERM_USE_ALL_COMMANDS = 0x00008000,
PC_PERM_RECEIVE_REQUESTS = 0x00010000,
PC_PERM_SHOW_BOSS = 0x00020000,
PC_PERM_DISABLE_PVM = 0x00040000,
PC_PERM_DISABLE_PVP = 0x00080000,
PC_PERM_DISABLE_CMD_DEAD = 0x00100000,
PC_PERM_CHANNEL_ADMIN = 0x00200000,
PC_PERM_TRADE_BOUNDED = 0x00400000,
PC_PERM_ITEM_UNCONDITIONAL = 0x00800000,
PC_PERM_ENABLE_COMMAND = 0x01000000,
PC_PERM_BYPASS_STAT_ONCLONE = 0x02000000,
PC_PERM_BYPASS_MAX_STAT = 0x04000000,
PC_PERM_ATTENDANCE = 0x08000000,
PC_PERM_TRADE = 0,
PC_PERM_PARTY,
PC_PERM_ALL_SKILL,
PC_PERM_USE_ALL_EQUIPMENT,
PC_PERM_SKILL_UNCONDITIONAL,
PC_PERM_JOIN_ALL_CHAT,
PC_PERM_NO_CHAT_KICK,
PC_PERM_HIDE_SESSION,
PC_PERM_WHO_DISPLAY_AID,
PC_PERM_RECEIVE_HACK_INFO,
PC_PERM_WARP_ANYWHERE,
PC_PERM_VIEW_HPMETER,
PC_PERM_VIEW_EQUIPMENT,
PC_PERM_USE_CHECK,
PC_PERM_USE_CHANGEMAPTYPE,
PC_PERM_USE_ALL_COMMANDS,
PC_PERM_RECEIVE_REQUESTS,
PC_PERM_SHOW_BOSS,
PC_PERM_DISABLE_PVM,
PC_PERM_DISABLE_PVP,
PC_PERM_DISABLE_CMD_DEAD,
PC_PERM_CHANNEL_ADMIN,
PC_PERM_TRADE_BOUNDED,
PC_PERM_ITEM_UNCONDITIONAL,
PC_PERM_ENABLE_COMMAND,
PC_PERM_BYPASS_STAT_ONCLONE,
PC_PERM_BYPASS_MAX_STAT,
PC_PERM_ATTENDANCE,
//.. add other here
PC_PERM_ALLPERMISSION = 0xFFFFFFFF,
PC_PERM_MAX,
};
static const struct s_pcg_permission_name {
const char *name;
enum e_pc_permission permission;
} pc_g_permission_name[] = {
} pc_g_permission_name[PC_PERM_MAX] = {
{ "can_trade", PC_PERM_TRADE },
{ "can_party", PC_PERM_PARTY },
{ "all_skill", PC_PERM_ALL_SKILL },
@ -88,7 +84,39 @@ static const struct s_pcg_permission_name {
{ "bypass_stat_onclone",PC_PERM_BYPASS_STAT_ONCLONE },
{ "bypass_max_stat",PC_PERM_BYPASS_MAX_STAT },
{ "attendance",PC_PERM_ATTENDANCE },
{ "all_permission", PC_PERM_ALLPERMISSION },
};
struct s_player_group{
uint32 id;
std::string name;
uint32 level;
bool log_commands;
std::vector<std::string> commands;
std::vector<std::string> char_commands;
std::bitset<PC_PERM_MAX> permissions;
uint32 index;
public:
bool can_use_command( const std::string& command, AtCommandType type );
bool has_permission( e_pc_permission permission );
bool should_log_commands();
};
class PlayerGroupDatabase : public TypesafeYamlDatabase<uint32, s_player_group>{
private:
std::map<uint32, std::vector<std::string>> inheritance;
bool parseCommands( const ryml::NodeRef& node, std::vector<std::string>& commands );
public:
PlayerGroupDatabase() : TypesafeYamlDatabase( "PLAYER_GROUP_DB", 1 ){
}
const std::string getDefaultLocation() override;
uint64 parseBodyNode( const ryml::NodeRef& node ) override;
void loadingFinished() override;
};
extern PlayerGroupDatabase player_group_db;
#endif /* PC_GROUPS_HPP */

View File

@ -5833,7 +5833,7 @@ BUILDIN_FUNC(warpparty)
}
// Fall through
case WARPPARTY_RANDOMALLAREA:
if(!mapdata->flag[MF_NORETURN] && !mapdata->flag[MF_NOWARP] && pc_job_can_entermap((enum e_job)pl_sd->status.class_, m, pl_sd->group_level)){
if(!mapdata->flag[MF_NORETURN] && !mapdata->flag[MF_NOWARP] && pc_job_can_entermap((enum e_job)pl_sd->status.class_, m, pc_get_group_level(pl_sd))){
if (rx || ry) {
int x1 = x + rx, y1 = y + ry,
x0 = x - rx, y0 = y - ry,
@ -5929,7 +5929,7 @@ BUILDIN_FUNC(warpguild)
pc_setpos(pl_sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT);
break;
case 3: // m,x,y
if(!map_getmapflag(pl_sd->bl.m, MF_NORETURN) && !map_getmapflag(pl_sd->bl.m, MF_NOWARP) && pc_job_can_entermap((enum e_job)pl_sd->status.class_, m, pl_sd->group_level))
if(!map_getmapflag(pl_sd->bl.m, MF_NORETURN) && !map_getmapflag(pl_sd->bl.m, MF_NOWARP) && pc_job_can_entermap((enum e_job)pl_sd->status.class_, m, pc_get_group_level(pl_sd)))
pc_setpos(pl_sd,mapindex,x,y,CLR_TELEPORT);
break;
}
@ -15205,8 +15205,8 @@ int atcommand_sub(struct script_state* st,int type) {
}
// Init Group ID, Level, & permissions
sd->group_id = sd->group_level = 99;
sd->permissions |= PC_PERM_ALLPERMISSION;
sd->group_id = 99;
pc_group_pc_load( sd );
}
if (!is_atcommand(fd, sd, cmd, type)) {
@ -24100,7 +24100,7 @@ BUILDIN_FUNC(jobcanentermap) {
jobid = sd->status.class_;
}
script_pushint(st, pc_job_can_entermap((enum e_job)jobid, m, sd ? sd->group_level : 0));
script_pushint(st, pc_job_can_entermap((enum e_job)jobid, m, sd ? pc_get_group_level(sd) : 0));
return SCRIPT_CMD_SUCCESS;
}

View File

@ -10100,7 +10100,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
if ((dstsd = g->member[i].sd) != NULL && sd != dstsd && !dstsd->state.autotrade && !pc_isdead(dstsd)) {
if (map_getmapflag(dstsd->bl.m, MF_NOWARP) && !map_flag_gvg2(dstsd->bl.m))
continue;
if (!pc_job_can_entermap((enum e_job)dstsd->status.class_, src->m, dstsd->group_level))
if (!pc_job_can_entermap((enum e_job)dstsd->status.class_, src->m, pc_get_group_level(dstsd)))
continue;
if(map_getcell(src->m,src->x+dx[j],src->y+dy[j],CELL_CHKNOREACH))
dx[j] = dy[j] = 0;
@ -14990,7 +14990,7 @@ static int skill_unit_onplace(struct skill_unit *unit, struct block_list *bl, t_
sg->val1 = (count<<16)|working;
if (pc_job_can_entermap((enum e_job)sd->status.class_, map_mapindex2mapid(m), sd->group_level))
if (pc_job_can_entermap((enum e_job)sd->status.class_, map_mapindex2mapid(m), pc_get_group_level(sd)))
pc_setpos(sd,m,x,y,CLR_TELEPORT);
}
} else if(bl->type == BL_MOB && battle_config.mob_warp&2) {
@ -20542,13 +20542,13 @@ static int skill_unit_timer_sub(DBKey key, DBData *data, va_list ap)
if(group->val1) {
sd = map_charid2sd(group->val1);
group->val1 = 0;
if (sd && !map_getmapflag(sd->bl.m, MF_NOWARP) && pc_job_can_entermap((enum e_job)sd->status.class_, unit->bl.m, sd->group_level))
if (sd && !map_getmapflag(sd->bl.m, MF_NOWARP) && pc_job_can_entermap((enum e_job)sd->status.class_, unit->bl.m, pc_get_group_level(sd)))
pc_setpos(sd,map_id2index(unit->bl.m),unit->bl.x,unit->bl.y,CLR_TELEPORT);
}
if(group->val2) {
sd = map_charid2sd(group->val2);
group->val2 = 0;
if (sd && !map_getmapflag(sd->bl.m, MF_NOWARP) && pc_job_can_entermap((enum e_job)sd->status.class_, unit->bl.m, sd->group_level))
if (sd && !map_getmapflag(sd->bl.m, MF_NOWARP) && pc_job_can_entermap((enum e_job)sd->status.class_, unit->bl.m, pc_get_group_level(sd)))
pc_setpos(sd,map_id2index(unit->bl.m),unit->bl.x,unit->bl.y,CLR_TELEPORT);
}
skill_delunit(unit);