400 lines
9.0 KiB
C++
400 lines
9.0 KiB
C++
// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
|
|
// For more information, see LICENCE in the main folder
|
|
|
|
#include "pc_groups.hpp"
|
|
|
|
#include <common/showmsg.hpp>
|
|
#include <common/socket.hpp> // set_eof
|
|
#include <common/strlib.hpp> // strcmpi
|
|
#include <common/utilities.hpp>
|
|
|
|
#include "atcommand.hpp" // AtCommandType
|
|
#include "pc.hpp" // map_session_data
|
|
|
|
using namespace rathena;
|
|
|
|
const std::string PlayerGroupDatabase::getDefaultLocation() {
|
|
return std::string( conf_path ) + "/groups.yml";
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if( !atcommand_exists( command.c_str() ) ){
|
|
this->invalidWarning( it, "Unknown atcommand: %s\n", command.c_str() );
|
|
return false;
|
|
}
|
|
|
|
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 );
|
|
}
|
|
}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 );
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
uint64 PlayerGroupDatabase::parseBodyNode( const ryml::NodeRef& node ){
|
|
uint32 groupId;
|
|
|
|
if( !this->asUInt32( node, "Id", groupId ) ){
|
|
return 0;
|
|
}
|
|
|
|
std::shared_ptr<s_player_group> group = this->find( groupId );
|
|
bool exists = group != nullptr;
|
|
|
|
if( !exists ){
|
|
if( !this->nodesExist( node, { "Name", "Level" })) {
|
|
return 0;
|
|
}
|
|
|
|
group = std::make_shared<s_player_group>();
|
|
group->id = groupId;
|
|
group->permissions.reset();
|
|
}
|
|
|
|
if( this->nodeExists( node, "Name" ) ){
|
|
std::string name;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
if( !found ){
|
|
this->invalidWarning( it, "Unknown permission: %s\n", str );
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( this->nodeExists( node, "Inherit" ) ){
|
|
const auto& inherits = node["Inherit"];
|
|
|
|
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;
|
|
}
|
|
|
|
std::shared_ptr<s_player_group> group = this->find( entry.first );
|
|
|
|
auto it = entry.second.begin();
|
|
|
|
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;
|
|
}
|
|
|
|
// Inherit atcommands
|
|
for( auto& command : otherGroup->commands ){
|
|
if( !util::vector_exists( group->commands, command ) ){
|
|
group->commands.push_back( command );
|
|
}
|
|
}
|
|
|
|
// Inherit charcommands
|
|
for( auto& command : otherGroup->char_commands ){
|
|
if( !util::vector_exists( group->char_commands, command ) ){
|
|
group->char_commands.push_back( command );
|
|
}
|
|
}
|
|
|
|
// 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();
|
|
|
|
TypesafeYamlDatabase::loadingFinished();
|
|
}
|
|
|
|
PlayerGroupDatabase player_group_db;
|
|
|
|
/**
|
|
* Checks if player group can use @/#command
|
|
* @param command Command name without @/# and params
|
|
* @param type enum AtCommanndType { COMMAND_ATCOMMAND = 1, COMMAND_CHARCOMMAND = 2 }
|
|
*/
|
|
bool s_player_group::can_use_command( const std::string& command, AtCommandType type ){
|
|
if( this->has_permission( PC_PERM_USE_ALL_COMMANDS ) ){
|
|
return true;
|
|
}
|
|
|
|
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(map_session_data * sd) {
|
|
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,
|
|
sd->group_id);
|
|
set_eof(sd->fd);
|
|
return;
|
|
}
|
|
|
|
sd->group = group;
|
|
sd->permissions = group->permissions;
|
|
}
|
|
|
|
/**
|
|
* Checks if player group has a permission
|
|
* @param permission permission to check
|
|
*/
|
|
bool s_player_group::has_permission( e_pc_permission permission ){
|
|
return this->permissions.test( permission );
|
|
}
|
|
|
|
/**
|
|
* Checks commands used by player group should be logged
|
|
*/
|
|
bool s_player_group::should_log_commands(){
|
|
return this->log_commands;
|
|
}
|
|
|
|
/**
|
|
* Initialize PC Groups and read config.
|
|
*/
|
|
void do_init_pc_groups( void ){
|
|
player_group_db.load();
|
|
}
|
|
|
|
/**
|
|
* Finalize PC Groups
|
|
*/
|
|
void do_final_pc_groups( void ){
|
|
player_group_db.clear();
|
|
}
|
|
|
|
/**
|
|
* Reload PC Groups
|
|
* Used in @reloadatcommand
|
|
*/
|
|
void pc_groups_reload( void ){
|
|
player_group_db.reload();
|
|
|
|
/* refresh online users permissions */
|
|
struct s_mapiterator* iter = mapit_getallusers();
|
|
for( map_session_data* sd = (map_session_data*)mapit_first(iter); mapit_exists(iter); sd = (map_session_data*)mapit_next(iter) ){
|
|
pc_group_pc_load(sd);
|
|
}
|
|
mapit_free(iter);
|
|
}
|