diff options
author | Mattes D <github@xoft.cz> | 2014-08-26 16:09:16 +0200 |
---|---|---|
committer | Mattes D <github@xoft.cz> | 2014-08-26 16:09:16 +0200 |
commit | 98974412fa6d7662b84555e079a6306dd4d90db3 (patch) | |
tree | 733c745ef60441671524169c1bdff022dbf86867 /src | |
parent | Profiling: Added profiling script for x64 executable. (diff) | |
parent | Merge pull request #1290 from mc-server/Ranks (diff) | |
download | cuberite-98974412fa6d7662b84555e079a6306dd4d90db3.tar cuberite-98974412fa6d7662b84555e079a6306dd4d90db3.tar.gz cuberite-98974412fa6d7662b84555e079a6306dd4d90db3.tar.bz2 cuberite-98974412fa6d7662b84555e079a6306dd4d90db3.tar.lz cuberite-98974412fa6d7662b84555e079a6306dd4d90db3.tar.xz cuberite-98974412fa6d7662b84555e079a6306dd4d90db3.tar.zst cuberite-98974412fa6d7662b84555e079a6306dd4d90db3.zip |
Diffstat (limited to '')
-rw-r--r-- | src/Bindings/AllToLua.pkg | 1 | ||||
-rw-r--r-- | src/Bindings/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/Bindings/ManualBindings.cpp | 153 | ||||
-rw-r--r-- | src/Bindings/ManualBindings.h | 18 | ||||
-rw-r--r-- | src/Bindings/ManualBindings_RankManager.cpp | 1007 | ||||
-rw-r--r-- | src/Bindings/gen_LuaState_Call.lua | 27 | ||||
-rw-r--r-- | src/CMakeLists.txt | 6 | ||||
-rw-r--r-- | src/Entities/Player.cpp | 262 | ||||
-rw-r--r-- | src/Entities/Player.h | 54 | ||||
-rw-r--r-- | src/Group.cpp | 41 | ||||
-rw-r--r-- | src/Group.h | 44 | ||||
-rw-r--r-- | src/GroupManager.cpp | 227 | ||||
-rw-r--r-- | src/GroupManager.h | 36 | ||||
-rw-r--r-- | src/Protocol/MojangAPI.cpp | 19 | ||||
-rw-r--r-- | src/Protocol/MojangAPI.h | 26 | ||||
-rw-r--r-- | src/RankManager.cpp | 1839 | ||||
-rw-r--r-- | src/RankManager.h | 246 | ||||
-rw-r--r-- | src/Root.cpp | 17 | ||||
-rw-r--r-- | src/Root.h | 9 | ||||
-rw-r--r-- | src/Server.cpp | 26 | ||||
-rw-r--r-- | src/Server.h | 2 |
21 files changed, 3395 insertions, 667 deletions
diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg index 88faa9dfc..37e6aecd2 100644 --- a/src/Bindings/AllToLua.pkg +++ b/src/Bindings/AllToLua.pkg @@ -67,7 +67,6 @@ $cfile "../Root.h" $cfile "../Cuboid.h" $cfile "../BoundingBox.h" $cfile "../Tracer.h" -$cfile "../Group.h" $cfile "../BlockArea.h" $cfile "../Generating/ChunkDesc.h" $cfile "../CraftingRecipes.h" diff --git a/src/Bindings/CMakeLists.txt b/src/Bindings/CMakeLists.txt index 54152668a..7a1769e9a 100644 --- a/src/Bindings/CMakeLists.txt +++ b/src/Bindings/CMakeLists.txt @@ -11,6 +11,7 @@ SET (SRCS LuaState.cpp LuaWindow.cpp ManualBindings.cpp + ManualBindings_RankManager.cpp Plugin.cpp PluginLua.cpp PluginManager.cpp @@ -96,7 +97,6 @@ set(BINDING_DEPENDENCIES ../Entities/HangingEntity.h ../Entities/ItemFrame.h ../Generating/ChunkDesc.h - ../Group.h ../Inventory.h ../Item.h ../ItemGrid.h diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 5aa76eee3..d56246dec 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -1803,49 +1803,30 @@ static int tolua_cWorld_ChunkStay(lua_State * tolua_S) -static int tolua_cPlayer_GetGroups(lua_State * tolua_S) +static int tolua_cPlayer_GetPermissions(lua_State * tolua_S) { - cPlayer * self = (cPlayer *)tolua_tousertype(tolua_S, 1, NULL); - - const cPlayer::GroupList & AllGroups = self->GetGroups(); + // Function signature: cPlayer:GetPermissions() -> {permissions-array} - lua_createtable(tolua_S, (int)AllGroups.size(), 0); - int newTable = lua_gettop(tolua_S); - int index = 1; - cPlayer::GroupList::const_iterator iter = AllGroups.begin(); - while (iter != AllGroups.end()) + // Check the params: + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cPlayer") || + !L.CheckParamEnd (2) + ) { - const cGroup * Group = *iter; - tolua_pushusertype(tolua_S, (void *)Group, "const cGroup"); - lua_rawseti(tolua_S, newTable, index); - ++iter; - ++index; + return 0; } - return 1; -} - - - - - -static int tolua_cPlayer_GetResolvedPermissions(lua_State * tolua_S) -{ - cPlayer * self = (cPlayer*) tolua_tousertype(tolua_S, 1, NULL); - cPlayer::StringList AllPermissions = self->GetResolvedPermissions(); - - lua_createtable(tolua_S, (int)AllPermissions.size(), 0); - int newTable = lua_gettop(tolua_S); - int index = 1; - cPlayer::StringList::iterator iter = AllPermissions.begin(); - while (iter != AllPermissions.end()) + // Get the params: + cPlayer * self = (cPlayer *)tolua_tousertype(tolua_S, 1, NULL); + if (self == NULL) { - std::string & Permission = *iter; - lua_pushlstring(tolua_S, Permission.c_str(), Permission.length()); - lua_rawseti(tolua_S, newTable, index); - ++iter; - ++index; + LOGWARNING("%s: invalid self (%p)", __FUNCTION__, self); + return 0; } + + // Push the permissions: + L.Push(self->GetPermissions()); return 1; } @@ -1902,6 +1883,40 @@ static int tolua_cPlayer_OpenWindow(lua_State * tolua_S) +static int tolua_cPlayer_PermissionMatches(lua_State * tolua_S) +{ + // Function signature: cPlayer:PermissionMatches(PermissionStr, TemplateStr) -> bool + + // Check the params: + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cPlayer") || + !L.CheckParamString (2, 3) || + !L.CheckParamEnd (4) + ) + { + return 0; + } + + // Get the params: + cPlayer * self = (cPlayer *)tolua_tousertype(tolua_S, 1, NULL); + if (self == NULL) + { + LOGWARNING("%s: invalid self (%p)", __FUNCTION__, self); + return 0; + } + AString Permission, Template; + L.GetStackValues(2, Permission, Template); + + // Push the result of the match: + L.Push(self->PermissionMatches(StringSplit(Permission, "."), StringSplit(Template, "."))); + return 1; +} + + + + + template < class OBJTYPE, void (OBJTYPE::*SetCallback)(cPluginLua * a_Plugin, int a_FnRef) @@ -2399,6 +2414,62 @@ static int tolua_cMojangAPI_GetUUIDsFromPlayerNames(lua_State * L) +static int tolua_cMojangAPI_MakeUUIDDashed(lua_State * L) +{ + // Function signature: cMojangAPI:MakeUUIDDashed(UUID) -> string + + // Check params: + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cMojangAPI") || + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) + { + return 0; + } + + // Get the params: + AString UUID; + S.GetStackValue(2, UUID); + + // Push the result: + S.Push(cRoot::Get()->GetMojangAPI().MakeUUIDDashed(UUID)); + return 1; +} + + + + + +static int tolua_cMojangAPI_MakeUUIDShort(lua_State * L) +{ + // Function signature: cMojangAPI:MakeUUIDShort(UUID) -> string + + // Check params: + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cMojangAPI") || + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) + { + return 0; + } + + // Get the params: + AString UUID; + S.GetStackValue(2, UUID); + + // Push the result: + S.Push(cRoot::Get()->GetMojangAPI().MakeUUIDShort(UUID)); + return 1; +} + + + + + static int Lua_ItemGrid_GetSlotCoords(lua_State * L) { tolua_Error tolua_err; @@ -3295,9 +3366,9 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cPlayer"); - tolua_function(tolua_S, "GetGroups", tolua_cPlayer_GetGroups); - tolua_function(tolua_S, "GetResolvedPermissions", tolua_cPlayer_GetResolvedPermissions); - tolua_function(tolua_S, "OpenWindow", tolua_cPlayer_OpenWindow); + tolua_function(tolua_S, "GetPermissions", tolua_cPlayer_GetPermissions); + tolua_function(tolua_S, "OpenWindow", tolua_cPlayer_OpenWindow); + tolua_function(tolua_S, "PermissionMatches", tolua_cPlayer_PermissionMatches); tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cLuaWindow"); @@ -3340,6 +3411,8 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_function(tolua_S, "GetPlayerNameFromUUID", tolua_cMojangAPI_GetPlayerNameFromUUID); tolua_function(tolua_S, "GetUUIDFromPlayerName", tolua_cMojangAPI_GetUUIDFromPlayerName); tolua_function(tolua_S, "GetUUIDsFromPlayerNames", tolua_cMojangAPI_GetUUIDsFromPlayerNames); + tolua_function(tolua_S, "MakeUUIDDashed", tolua_cMojangAPI_MakeUUIDDashed); + tolua_function(tolua_S, "MakeUUIDShort", tolua_cMojangAPI_MakeUUIDShort); tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cItemGrid"); @@ -3347,6 +3420,8 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_endmodule(tolua_S); tolua_function(tolua_S, "md5", tolua_md5); + + BindRankManager(tolua_S); tolua_endmodule(tolua_S); } diff --git a/src/Bindings/ManualBindings.h b/src/Bindings/ManualBindings.h index 36161c6a2..1b6e65654 100644 --- a/src/Bindings/ManualBindings.h +++ b/src/Bindings/ManualBindings.h @@ -1,8 +1,24 @@ #pragma once struct lua_State; + + + + + +/** Provides namespace for the bindings. */ class ManualBindings { public: - static void Bind( lua_State* tolua_S); + /** Binds all the manually implemented functions to tolua_S. */ + static void Bind(lua_State * tolua_S); + +protected: + /** Binds the manually implemented cRankManager glue code to tolua_S. + Implemented in ManualBindings_RankManager.cpp. */ + static void BindRankManager(lua_State * tolua_S); }; + + + + diff --git a/src/Bindings/ManualBindings_RankManager.cpp b/src/Bindings/ManualBindings_RankManager.cpp new file mode 100644 index 000000000..2e93ad264 --- /dev/null +++ b/src/Bindings/ManualBindings_RankManager.cpp @@ -0,0 +1,1007 @@ + +// ManualBindings_RankManager.cpp + +// Implements the cRankManager Lua bindings + +#include "Globals.h" +#include "ManualBindings.h" +#include "../Root.h" +#include "tolua++/include/tolua++.h" +#include "LuaState.h" + + + + + +/** Binds cRankManager::AddGroup */ +static int tolua_cRankManager_AddGroup(lua_State * L) +{ + // Function signature: + // cRankManager:AddGroup(GroupName) + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) + { + return 0; + } + + // Read the params: + AString GroupName; + S.GetStackValue(2, GroupName); + + // Add the group: + cRoot::Get()->GetRankManager().AddGroup(GroupName); + return 0; +} + + + + + +/** Binds cRankManager::AddGroupToRank */ +static int tolua_cRankManager_AddGroupToRank(lua_State * L) +{ + // Function signature: + // cRankManager:AddGroupToRank(GroupName, RankName) -> bool + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2, 3) || + !S.CheckParamEnd(4) + ) + { + return 0; + } + + // Read the params: + AString GroupName, RankName; + S.GetStackValues(2, GroupName, RankName); + + // Add the group to the rank: + S.Push(cRoot::Get()->GetRankManager().AddGroupToRank(GroupName, RankName)); + return 1; +} + + + + + +/** Binds cRankManager::AddPermissionToGroup */ +static int tolua_cRankManager_AddPermissionToGroup(lua_State * L) +{ + // Function signature: + // cRankManager:AddPermissionToGroup(Permission, GroupName) -> bool + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2, 3) || + !S.CheckParamEnd(4) + ) + { + return 0; + } + + // Read the params: + AString GroupName, Permission; + S.GetStackValues(2, Permission, GroupName); + + // Add the group to the rank: + S.Push(cRoot::Get()->GetRankManager().AddPermissionToGroup(Permission, GroupName)); + return 1; +} + + + + + +/** Binds cRankManager::AddRank */ +static int tolua_cRankManager_AddRank(lua_State * L) +{ + // Function signature: + // cRankManager:AddRank(RankName) + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2, 5) || + !S.CheckParamEnd(6) + ) + { + return 0; + } + + // Read the params: + AString RankName, MsgPrefix, MsgSuffix, MsgNameColorCode; + S.GetStackValues(2, RankName, MsgPrefix, MsgSuffix, MsgNameColorCode); + + // Add the rank: + cRoot::Get()->GetRankManager().AddRank(RankName, MsgPrefix, MsgSuffix, MsgNameColorCode); + return 0; +} + + + + + +/** Binds cRankManager::GetAllGroups */ +static int tolua_cRankManager_GetAllGroups(lua_State * L) +{ + // Function signature: + // cRankManager:GetAllGroups() -> arraytable of GroupNames + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamEnd(2) + ) + { + return 0; + } + + // Get the groups: + AStringVector Groups = cRoot::Get()->GetRankManager().GetAllGroups(); + + // Push the results: + S.Push(Groups); + return 1; +} + + + + + +/** Binds cRankManager::GetAllPermissions */ +static int tolua_cRankManager_GetAllPermissions(lua_State * L) +{ + // Function signature: + // cRankManager:GetAllPermissions() -> arraytable of Permissions + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamEnd(2) + ) + { + return 0; + } + + // Get the permissions: + AStringVector Permissions = cRoot::Get()->GetRankManager().GetAllPermissions(); + + // Push the results: + S.Push(Permissions); + return 1; +} + + + + + +/** Binds cRankManager::GetAllRanks */ +static int tolua_cRankManager_GetAllRanks(lua_State * L) +{ + // Function signature: + // cRankManager:GetAllRanks() -> arraytable of RankNames + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamEnd(2) + ) + { + return 0; + } + + // Get the ranks: + AStringVector Ranks = cRoot::Get()->GetRankManager().GetAllRanks(); + + // Push the results: + S.Push(Ranks); + return 1; +} + + + + + +/** Binds cRankManager::GetDefaultRank */ +static int tolua_cRankManager_GetDefaultRank(lua_State * L) +{ + // Function signature: + // cRankManager:GetDefaultRank() -> string + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamEnd(2) + ) + { + return 0; + } + + // Return the rank name: + S.Push(cRoot::Get()->GetRankManager().GetDefaultRank()); + return 1; +} + + + + + +/** Binds cRankManager::GetGroupPermissions */ +static int tolua_cRankManager_GetGroupPermissions(lua_State * L) +{ + // Function signature: + // cRankManager:GetGroupPermissions(GroupName) -> arraytable of permissions + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) + { + return 0; + } + + // Get the params: + AString GroupName; + S.GetStackValue(2, GroupName); + + // Get the permissions: + AStringVector Permissions = cRoot::Get()->GetRankManager().GetGroupPermissions(GroupName); + + // Push the results: + S.Push(Permissions); + return 1; +} + + + + + +/** Binds cRankManager::GetPlayerGroups */ +static int tolua_cRankManager_GetPlayerGroups(lua_State * L) +{ + // Function signature: + // cRankManager:GetPlayerGroups(PlayerUUID) -> arraytable of GroupNames + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) + { + return 0; + } + + // Get the params: + AString PlayerUUID; + S.GetStackValue(2, PlayerUUID); + + // Get the groups: + AStringVector Groups = cRoot::Get()->GetRankManager().GetPlayerGroups(PlayerUUID); + + // Push the results: + S.Push(Groups); + return 1; +} + + + + + +/** Binds cRankManager::GetPlayerMsgVisuals */ +static int tolua_cRankManager_GetPlayerMsgVisuals(lua_State * L) +{ + // Function signature: + // cRankManager:GetPlayerMsgVisuals(PlayerUUID) -> string, string, string + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) + { + return 0; + } + + // Get the params: + AString PlayerUUID; + S.GetStackValue(2, PlayerUUID); + + // Get the permissions: + AString MsgPrefix, MsgSuffix, MsgNameColorCode; + if (!cRoot::Get()->GetRankManager().GetPlayerMsgVisuals(PlayerUUID, MsgPrefix, MsgSuffix, MsgNameColorCode)) + { + return 0; + } + + // Push the results: + S.Push(MsgPrefix); + S.Push(MsgSuffix); + S.Push(MsgNameColorCode); + return 3; +} + + + + + +/** Binds cRankManager::GetPlayerPermissions */ +static int tolua_cRankManager_GetPlayerPermissions(lua_State * L) +{ + // Function signature: + // cRankManager:GetPlayerPermissions(PlayerUUID) -> arraytable of permissions + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) + { + return 0; + } + + // Get the params: + AString PlayerUUID; + S.GetStackValue(2, PlayerUUID); + + // Get the permissions: + AStringVector Permissions = cRoot::Get()->GetRankManager().GetPlayerPermissions(PlayerUUID); + + // Push the results: + S.Push(Permissions); + return 1; +} + + + + + +/** Binds cRankManager::GetPlayerRankName */ +static int tolua_cRankManager_GetPlayerRankName(lua_State * L) +{ + // Function signature: + // cRankManager:GetPlayerRankName(PlayerUUID) -> string + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) + { + return 0; + } + + // Get the params: + AString PlayerUUID; + S.GetStackValue(2, PlayerUUID); + + // Get the rank name: + AString RankName = cRoot::Get()->GetRankManager().GetPlayerRankName(PlayerUUID); + + // Push the result: + S.Push(RankName); + return 1; +} + + + + + +/** Binds cRankManager::GetRankGroups */ +static int tolua_cRankManager_GetRankGroups(lua_State * L) +{ + // Function signature: + // cRankManager:GetRankGroups(RankName) -> arraytable of groupnames + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) + { + return 0; + } + + // Get the params: + AString RankName; + S.GetStackValue(2, RankName); + + // Get the groups: + AStringVector Groups = cRoot::Get()->GetRankManager().GetRankGroups(RankName); + + // Push the results: + S.Push(Groups); + return 1; +} + + + + + +/** Binds cRankManager::GetRankPermissions */ +static int tolua_cRankManager_GetRankPermissions(lua_State * L) +{ + // Function signature: + // cRankManager:GetRankPermissions(RankName) -> arraytable of permissions + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) + { + return 0; + } + + // Get the params: + AString RankName; + S.GetStackValue(2, RankName); + + // Get the permissions: + AStringVector Permissions = cRoot::Get()->GetRankManager().GetRankPermissions(RankName); + + // Push the results: + S.Push(Permissions); + return 1; +} + + + + + +/** Binds cRankManager::GetRankVisuals */ +static int tolua_cRankManager_GetRankVisuals(lua_State * L) +{ + // Function signature: + // cRankManager:GetRankVisuals(RankName) -> MsgPrefix, MsgSuffix, MsgNameColorCode + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) + { + return 0; + } + + // Get the params: + AString RankName; + S.GetStackValue(2, RankName); + + // Get the visuals: + AString MsgPrefix, MsgSuffix, MsgNameColorCode; + if (!cRoot::Get()->GetRankManager().GetRankVisuals(RankName, MsgPrefix, MsgSuffix, MsgNameColorCode)) + { + // No such rank, return nothing: + return 0; + } + + // Push the results: + S.Push(MsgPrefix); + S.Push(MsgSuffix); + S.Push(MsgNameColorCode); + return 3; +} + + + + + +/** Binds cRankManager::GroupExists */ +static int tolua_cRankManager_GroupExists(lua_State * L) +{ + // Function signature: + // cRankManager:GroupExists(GroupName) -> bool + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) + { + return 0; + } + + // Get the params: + AString GroupName; + S.GetStackValue(2, GroupName); + + // Get the response: + bool res = cRoot::Get()->GetRankManager().GroupExists(GroupName); + + // Push the result: + S.Push(res); + return 1; +} + + + + + +/** Binds cRankManager::IsGroupInRank */ +static int tolua_cRankManager_IsGroupInRank(lua_State * L) +{ + // Function signature: + // cRankManager:IsGroupInRank(GroupName, RankName) -> bool + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2, 3) || + !S.CheckParamEnd(4) + ) + { + return 0; + } + + // Get the params: + AString GroupName, RankName; + S.GetStackValues(2, GroupName, RankName); + + // Get the response: + bool res = cRoot::Get()->GetRankManager().IsGroupInRank(GroupName, RankName); + + // Push the result: + S.Push(res); + return 1; +} + + + + + +/** Binds cRankManager::IsPermissionInGroup */ +static int tolua_cRankManager_IsPermissionInGroup(lua_State * L) +{ + // Function signature: + // cRankManager:IsPermissionInGroup(Permission, GroupName) -> bool + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2, 3) || + !S.CheckParamEnd(4) + ) + { + return 0; + } + + // Get the params: + AString GroupName, Permission; + S.GetStackValues(2, Permission, GroupName); + + // Get the response: + bool res = cRoot::Get()->GetRankManager().IsPermissionInGroup(Permission, GroupName); + + // Push the result: + S.Push(res); + return 1; +} + + + + + +/** Binds cRankManager::IsPlayerRankSet */ +static int tolua_cRankManager_IsPlayerRankSet(lua_State * L) +{ + // Function signature: + // cRankManager:IsPlayerRankSet(PlayerUUID) -> bool + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) + { + return 0; + } + + // Get the params: + AString PlayerUUID; + S.GetStackValue(2, PlayerUUID); + + // Get the response: + bool res = cRoot::Get()->GetRankManager().IsPlayerRankSet(PlayerUUID); + + // Push the result: + S.Push(res); + return 1; +} + + + + + +/** Binds cRankManager::RankExists */ +static int tolua_cRankManager_RankExists(lua_State * L) +{ + // Function signature: + // cRankManager:RankExists(RankName) -> bool + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) + { + return 0; + } + + // Get the params: + AString RankName; + S.GetStackValue(2, RankName); + + // Get the response: + bool res = cRoot::Get()->GetRankManager().RankExists(RankName); + + // Push the result: + S.Push(res); + return 1; +} + + + + + +/** Binds cRankManager::RemoveGroup */ +static int tolua_cRankManager_RemoveGroup(lua_State * L) +{ + // Function signature: + // cRankManager:RemoveGroup(GroupName) + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) + { + return 0; + } + + // Get the params: + AString GroupName; + S.GetStackValue(2, GroupName); + + // Remove the group: + cRoot::Get()->GetRankManager().RemoveGroup(GroupName); + return 0; +} + + + + + +/** Binds cRankManager::RemoveGroupFromRank */ +static int tolua_cRankManager_RemoveGroupFromRank(lua_State * L) +{ + // Function signature: + // cRankManager:RemoveGroupFromRank(GroupName, RankName) + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2, 3) || + !S.CheckParamEnd(4) + ) + { + return 0; + } + + // Get the params: + AString GroupName, RankName; + S.GetStackValues(2, GroupName, RankName); + + // Remove the group: + cRoot::Get()->GetRankManager().RemoveGroupFromRank(GroupName, RankName); + return 0; +} + + + + + +/** Binds cRankManager::RemovePermissionFromGroup */ +static int tolua_cRankManager_RemovePermissionFromGroup(lua_State * L) +{ + // Function signature: + // cRankManager:RemovePermissionFromGroup(Permission, GroupName) + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2, 3) || + !S.CheckParamEnd(4) + ) + { + return 0; + } + + // Get the params: + AString GroupName, Permission; + S.GetStackValues(2, Permission, GroupName); + + // Remove the group: + cRoot::Get()->GetRankManager().RemovePermissionFromGroup(Permission, GroupName); + return 0; +} + + + + + +/** Binds cRankManager::RemovePlayerRank */ +static int tolua_cRankManager_RemovePlayerRank(lua_State * L) +{ + // Function signature: + // cRankManager:RemovePlayerRank(PlayerUUID) + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) + { + return 0; + } + + // Get the params: + AString PlayerUUID; + S.GetStackValue(2, PlayerUUID); + + // Remove the player's rank: + cRoot::Get()->GetRankManager().RemovePlayerRank(PlayerUUID); + return 0; +} + + + + + +/** Binds cRankManager::RemoveRank */ +static int tolua_cRankManager_RemoveRank(lua_State * L) +{ + // Function signature: + // cRankManager:RemoveRank(RankName, [ReplacementRankName]) + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2) || + // Param 3 is otpional, defaults to nil -> empty string + !S.CheckParamEnd(4) + ) + { + return 0; + } + + // Get the params: + AString RankName, ReplacementRankName; + S.GetStackValues(2, RankName, ReplacementRankName); + + // Remove the rank: + cRoot::Get()->GetRankManager().RemoveRank(RankName, ReplacementRankName); + return 0; +} + + + + + +/** Binds cRankManager::RenameGroup */ +static int tolua_cRankManager_RenameGroup(lua_State * L) +{ + // Function signature: + // cRankManager:RenameGroup(OldName, NewName) + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2, 3) || + !S.CheckParamEnd(4) + ) + { + return 0; + } + + // Get the params: + AString OldName, NewName; + S.GetStackValues(2, OldName, NewName); + + // Remove the group: + bool res = cRoot::Get()->GetRankManager().RenameGroup(OldName, NewName); + + // Push the result: + S.Push(res); + return 1; +} + + + + + +/** Binds cRankManager::RenameRank */ +static int tolua_cRankManager_RenameRank(lua_State * L) +{ + // Function signature: + // cRankManager:RenameRank(OldName, NewName) + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2, 3) || + !S.CheckParamEnd(4) + ) + { + return 0; + } + + // Get the params: + AString OldName, NewName; + S.GetStackValues(2, OldName, NewName); + + // Remove the rank: + bool res = cRoot::Get()->GetRankManager().RenameRank(OldName, NewName); + + // Push the result: + S.Push(res); + return 1; +} + + + + + +/** Binds cRankManager::SetDefaultRank */ +static int tolua_cRankManager_SetDefaultRank(lua_State * L) +{ + // Function signature: + // cRankManager:SetDefaultRank(RankName) -> bool + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) + { + return 0; + } + + // Get the params: + AString RankName; + S.GetStackValue(2, RankName); + + // Set the rank, return the result: + S.Push(cRoot::Get()->GetRankManager().SetDefaultRank(RankName)); + return 0; +} + + + + + +/** Binds cRankManager::SetPlayerRank */ +static int tolua_cRankManager_SetPlayerRank(lua_State * L) +{ + // Function signature: + // cRankManager:SetPlayerRank(PlayerUUID, PlayerName, RankName) + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2, 4) || + !S.CheckParamEnd(5) + ) + { + return 0; + } + + // Get the params: + AString PlayerUUID, PlayerName, RankName; + S.GetStackValues(2, PlayerUUID, PlayerName, RankName); + + // Set the rank: + cRoot::Get()->GetRankManager().SetPlayerRank(PlayerUUID, PlayerName, RankName); + return 0; +} + + + + + +/** Binds cRankManager::SetRankVisuals */ +static int tolua_cRankManager_SetRankVisuals(lua_State * L) +{ + // Function signature: + // cRankManager:SetRankVisuals(RankName, MsgPrefix, MsgSuffix, MsgNameColorCode) + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2, 5) || + !S.CheckParamEnd(6) + ) + { + return 0; + } + + // Get the params: + AString RankName, MsgPrefix, MsgSuffix, MsgNameColorCode; + S.GetStackValues(2, RankName, MsgPrefix, MsgSuffix, MsgNameColorCode); + + // Set the visuals: + cRoot::Get()->GetRankManager().SetRankVisuals(RankName, MsgPrefix, MsgSuffix, MsgNameColorCode); + return 0; +} + + + + + +void ManualBindings::BindRankManager(lua_State * tolua_S) +{ + // Create the cRankManager class in the API: + tolua_usertype(tolua_S, "cRankManager"); + tolua_cclass(tolua_S, "cRankManager", "cRankManager", "", NULL); + + // Fill in the functions (alpha-sorted): + tolua_beginmodule(tolua_S, "cRankManager"); + tolua_function(tolua_S, "AddGroup", tolua_cRankManager_AddGroup); + tolua_function(tolua_S, "AddGroupToRank", tolua_cRankManager_AddGroupToRank); + tolua_function(tolua_S, "AddPermissionToGroup", tolua_cRankManager_AddPermissionToGroup); + tolua_function(tolua_S, "AddRank", tolua_cRankManager_AddRank); + tolua_function(tolua_S, "GetAllGroups", tolua_cRankManager_GetAllGroups); + tolua_function(tolua_S, "GetAllPermissions", tolua_cRankManager_GetAllPermissions); + tolua_function(tolua_S, "GetAllRanks", tolua_cRankManager_GetAllRanks); + tolua_function(tolua_S, "GetDefaultRank", tolua_cRankManager_GetDefaultRank); + tolua_function(tolua_S, "GetGroupPermissions", tolua_cRankManager_GetGroupPermissions); + tolua_function(tolua_S, "GetPlayerGroups", tolua_cRankManager_GetPlayerGroups); + tolua_function(tolua_S, "GetPlayerMsgVisuals", tolua_cRankManager_GetPlayerMsgVisuals); + tolua_function(tolua_S, "GetPlayerPermissions", tolua_cRankManager_GetPlayerPermissions); + tolua_function(tolua_S, "GetPlayerRankName", tolua_cRankManager_GetPlayerRankName); + tolua_function(tolua_S, "GetRankGroups", tolua_cRankManager_GetRankGroups); + tolua_function(tolua_S, "GetRankPermissions", tolua_cRankManager_GetRankPermissions); + tolua_function(tolua_S, "GetRankVisuals", tolua_cRankManager_GetRankVisuals); + tolua_function(tolua_S, "GroupExists", tolua_cRankManager_GroupExists); + tolua_function(tolua_S, "IsGroupInRank", tolua_cRankManager_IsGroupInRank); + tolua_function(tolua_S, "IsPermissionInGroup", tolua_cRankManager_IsPermissionInGroup); + tolua_function(tolua_S, "IsPlayerRankSet", tolua_cRankManager_IsPlayerRankSet); + tolua_function(tolua_S, "RankExists", tolua_cRankManager_RankExists); + tolua_function(tolua_S, "RemoveGroup", tolua_cRankManager_RemoveGroup); + tolua_function(tolua_S, "RemoveGroupFromRank", tolua_cRankManager_RemoveGroupFromRank); + tolua_function(tolua_S, "RemovePermissionFromGroup", tolua_cRankManager_RemovePermissionFromGroup); + tolua_function(tolua_S, "RemovePlayerRank", tolua_cRankManager_RemovePlayerRank); + tolua_function(tolua_S, "RemoveRank", tolua_cRankManager_RemoveRank); + tolua_function(tolua_S, "RenameGroup", tolua_cRankManager_RenameGroup); + tolua_function(tolua_S, "RenameRank", tolua_cRankManager_RenameRank); + tolua_function(tolua_S, "SetDefaultRank", tolua_cRankManager_SetDefaultRank); + tolua_function(tolua_S, "SetPlayerRank", tolua_cRankManager_SetPlayerRank); + tolua_function(tolua_S, "SetRankVisuals", tolua_cRankManager_SetRankVisuals); + tolua_endmodule(tolua_S); +} + + + + diff --git a/src/Bindings/gen_LuaState_Call.lua b/src/Bindings/gen_LuaState_Call.lua index 13ef8b882..2d8630d12 100644 --- a/src/Bindings/gen_LuaState_Call.lua +++ b/src/Bindings/gen_LuaState_Call.lua @@ -183,6 +183,33 @@ for _, combination in ipairs(Combinations) do WriteOverload(f, combination[1], combination[2]) end +-- Generate the cLuaState::GetStackValues() multi-param templates: +for i = 2, 6 do + f:write("/** Reads ", i, " consecutive values off the stack */\ntemplate <\n") + + -- Write the template function header: + local txt = {} + for idx = 1, i do + table.insert(txt, "\ttypename ArgT" .. idx) + end + f:write(table.concat(txt, ",\n")) + + -- Write the argument declarations: + txt = {} + f:write("\n>\nvoid GetStackValues(\n\tint a_BeginPos,\n") + for idx = 1, i do + table.insert(txt, "\tArgT" .. idx .. " & Arg" .. idx) + end + f:write(table.concat(txt, ",\n")) + + -- Write the function body: + f:write("\n)\n{\n") + for idx = 1, i do + f:write("\tGetStackValue(a_BeginPos + ", idx - 1, ", Arg", idx, ");\n") + end + f:write("}\n\n\n\n\n\n") +end + -- Close the generated file f:close() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6b5efbd1f..37657ba91 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -34,8 +34,6 @@ SET (SRCS FastRandom.cpp FurnaceRecipe.cpp Globals.cpp - Group.cpp - GroupManager.cpp Inventory.cpp Item.cpp ItemGrid.cpp @@ -53,6 +51,7 @@ SET (SRCS MonsterConfig.cpp Noise.cpp ProbabDistrib.cpp + RankManager.cpp RCONServer.cpp Root.cpp Scoreboard.cpp @@ -98,8 +97,6 @@ SET (HDRS ForEachChunkProvider.h FurnaceRecipe.h Globals.h - Group.h - GroupManager.h Inventory.h Item.h ItemGrid.h @@ -122,6 +119,7 @@ SET (HDRS MonsterConfig.h Noise.h ProbabDistrib.h + RankManager.h RCONServer.h Root.h Scoreboard.h diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 32290885d..b0dd40615 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -10,8 +10,6 @@ #include "../Bindings/PluginManager.h" #include "../BlockEntities/BlockEntity.h" #include "../BlockEntities/EnderChestEntity.h" -#include "../GroupManager.h" -#include "../Group.h" #include "../Root.h" #include "../OSSupport/Timer.h" #include "../Chunk.h" @@ -59,7 +57,6 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) : m_EnderChestContents(9, 3), m_CurrentWindow(NULL), m_InventoryWindow(NULL), - m_Color('-'), m_GameMode(eGameMode_NotSet), m_IP(""), m_ClientHandle(a_Client), @@ -1367,48 +1364,6 @@ void cPlayer::SetVisible(bool a_bVisible) -void cPlayer::AddToGroup( const AString & a_GroupName) -{ - cGroup* Group = cRoot::Get()->GetGroupManager()->GetGroup( a_GroupName); - m_Groups.push_back( Group); - LOGD("Added %s to group %s", GetName().c_str(), a_GroupName.c_str()); - ResolveGroups(); - ResolvePermissions(); -} - - - - - -void cPlayer::RemoveFromGroup( const AString & a_GroupName) -{ - bool bRemoved = false; - for (GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr) - { - if ((*itr)->GetName().compare(a_GroupName) == 0) - { - m_Groups.erase( itr); - bRemoved = true; - break; - } - } - - if (bRemoved) - { - LOGD("Removed %s from group %s", GetName().c_str(), a_GroupName.c_str()); - ResolveGroups(); - ResolvePermissions(); - } - else - { - LOGWARN("Tried to remove %s from group %s but was not in that group", GetName().c_str(), a_GroupName.c_str()); - } -} - - - - - bool cPlayer::HasPermission(const AString & a_Permission) { if (a_Permission.empty()) @@ -1417,33 +1372,18 @@ bool cPlayer::HasPermission(const AString & a_Permission) return true; } - AStringVector Split = StringSplit( a_Permission, "."); - PermissionMap Possibilities = m_ResolvedPermissions; - // Now search the namespaces - while (Possibilities.begin() != Possibilities.end()) + AStringVector Split = StringSplit(a_Permission, "."); + + // Iterate over all granted permissions; if any matches, then return success: + for (AStringVectorVector::const_iterator itr = m_SplitPermissions.begin(), end = m_SplitPermissions.end(); itr != end; ++itr) { - PermissionMap::iterator itr = Possibilities.begin(); - if (itr->second) + if (PermissionMatches(Split, *itr)) { - AStringVector OtherSplit = StringSplit( itr->first, "."); - if (OtherSplit.size() <= Split.size()) - { - unsigned int i; - for (i = 0; i < OtherSplit.size(); ++i) - { - if (OtherSplit[i].compare( Split[i]) != 0) - { - if (OtherSplit[i].compare("*") == 0) return true; // WildCard man!! WildCard! - break; - } - } - if (i == Split.size()) return true; - } + return true; } - Possibilities.erase( itr); - } + } // for itr - m_SplitPermissions[] - // Nothing that matched :( + // No granted permission matches return false; } @@ -1451,82 +1391,35 @@ bool cPlayer::HasPermission(const AString & a_Permission) -bool cPlayer::IsInGroup( const AString & a_Group) +bool cPlayer::PermissionMatches(const AStringVector & a_Permission, const AStringVector & a_Template) { - for (GroupList::iterator itr = m_ResolvedGroups.begin(); itr != m_ResolvedGroups.end(); ++itr) + // Check the sub-items if they are the same or there's a wildcard: + size_t lenP = a_Permission.size(); + size_t lenT = a_Template.size(); + size_t minLen = std::min(lenP, lenT); + for (size_t i = 0; i < minLen; i++) { - if (a_Group.compare( (*itr)->GetName().c_str()) == 0) + if (a_Template[i] == "*") + { + // Has matched so far and now there's a wildcard in the template, so the permission matches: return true; - } - return false; -} - - - - - -void cPlayer::ResolvePermissions() -{ - m_ResolvedPermissions.clear(); // Start with an empty map - - // Copy all player specific permissions into the resolved permissions map - for (PermissionMap::iterator itr = m_Permissions.begin(); itr != m_Permissions.end(); ++itr) - { - m_ResolvedPermissions[ itr->first ] = itr->second; - } - - for (GroupList::iterator GroupItr = m_ResolvedGroups.begin(); GroupItr != m_ResolvedGroups.end(); ++GroupItr) - { - const cGroup::PermissionMap & Permissions = (*GroupItr)->GetPermissions(); - for (cGroup::PermissionMap::const_iterator itr = Permissions.begin(); itr != Permissions.end(); ++itr) + } + if (a_Permission[i] != a_Template[i]) { - m_ResolvedPermissions[ itr->first ] = itr->second; + // Found a mismatch + return false; } } -} - - - - -void cPlayer::ResolveGroups() -{ - // Clear resolved groups first - m_ResolvedGroups.clear(); - - // Get a complete resolved list of all groups the player is in - std::map< cGroup*, bool > AllGroups; // Use a map, because it's faster than iterating through a list to find duplicates - GroupList ToIterate; - for (GroupList::iterator GroupItr = m_Groups.begin(); GroupItr != m_Groups.end(); ++GroupItr) + // So far all the sub-items have matched + // If the sub-item count is the same, then the permission matches: + if (lenP == lenT) { - ToIterate.push_back( *GroupItr); - } - while (ToIterate.begin() != ToIterate.end()) - { - cGroup* CurrentGroup = *ToIterate.begin(); - if (AllGroups.find( CurrentGroup) != AllGroups.end()) - { - LOGWARNING("ERROR: Player \"%s\" is in the group multiple times (\"%s\"). Please fix your settings in users.ini!", - GetName().c_str(), CurrentGroup->GetName().c_str() - ); - } - else - { - AllGroups[ CurrentGroup ] = true; - m_ResolvedGroups.push_back( CurrentGroup); // Add group to resolved list - const cGroup::GroupList & Inherits = CurrentGroup->GetInherits(); - for (cGroup::GroupList::const_iterator itr = Inherits.begin(); itr != Inherits.end(); ++itr) - { - if (AllGroups.find( *itr) != AllGroups.end()) - { - LOGERROR("ERROR: Player %s is in the same group multiple times due to inheritance (%s). FIX IT!", GetName().c_str(), (*itr)->GetName().c_str()); - continue; - } - ToIterate.push_back( *itr); - } - } - ToIterate.erase( ToIterate.begin()); + return true; } + + // There are more sub-items in either the permission or the template, not a match: + return false; } @@ -1535,17 +1428,14 @@ void cPlayer::ResolveGroups() AString cPlayer::GetColor(void) const { - if (m_Color != '-') + if (m_MsgNameColorCode.empty() || (m_MsgNameColorCode == "-")) { - return cChatColor::Delimiter + m_Color; + // Color has not been assigned, return an empty string: + return AString(); } - if (m_Groups.size() < 1) - { - return cChatColor::White; - } - - return (*m_Groups.begin())->GetColor(); + // Return the color, including the delimiter: + return cChatColor::Delimiter + m_MsgNameColorCode; } @@ -1661,48 +1551,9 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn) -void cPlayer::LoadPermissionsFromDisk() -{ - m_Groups.clear(); - m_Permissions.clear(); - - cIniFile IniFile; - if (IniFile.ReadFile("users.ini")) - { - AString Groups = IniFile.GetValueSet(GetName(), "Groups", "Default"); - AStringVector Split = StringSplitAndTrim(Groups, ","); - - for (AStringVector::const_iterator itr = Split.begin(), end = Split.end(); itr != end; ++itr) - { - if (!cRoot::Get()->GetGroupManager()->ExistsGroup(*itr)) - { - LOGWARNING("The group %s for player %s was not found!", itr->c_str(), GetName().c_str()); - } - AddToGroup(*itr); - } - - AString Color = IniFile.GetValue(GetName(), "Color", "-"); - if (!Color.empty()) - { - m_Color = Color[0]; - } - } - else - { - cGroupManager::GenerateDefaultUsersIni(IniFile); - IniFile.AddValue("Groups", GetName(), "Default"); - AddToGroup("Default"); - } - IniFile.WriteFile("users.ini"); - ResolvePermissions(); -} - - - - bool cPlayer::LoadFromDisk(cWorldPtr & a_World) { - LoadPermissionsFromDisk(); + LoadRank(); // Load from the UUID file: if (LoadFromFile(GetUUIDFileName(m_UUID), a_World)) @@ -1937,26 +1788,6 @@ bool cPlayer::SaveToDisk() -cPlayer::StringList cPlayer::GetResolvedPermissions() -{ - StringList Permissions; - - const PermissionMap& ResolvedPermissions = m_ResolvedPermissions; - for (PermissionMap::const_iterator itr = ResolvedPermissions.begin(); itr != ResolvedPermissions.end(); ++itr) - { - if (itr->second) - { - Permissions.push_back( itr->first); - } - } - - return Permissions; -} - - - - - void cPlayer::UseEquippedItem(int a_Amount) { if (IsGameModeCreative()) // No damage in creative @@ -2215,6 +2046,31 @@ void cPlayer::ApplyFoodExhaustionFromMovement() +void cPlayer::LoadRank(void) +{ + // Load the values from cRankManager: + cRankManager & RankMgr = cRoot::Get()->GetRankManager(); + m_Rank = RankMgr.GetPlayerRankName(m_UUID); + if (m_Rank.empty()) + { + m_Rank = RankMgr.GetDefaultRank(); + } + m_Permissions = RankMgr.GetPlayerPermissions(m_UUID); + RankMgr.GetRankVisuals(m_Rank, m_MsgPrefix, m_MsgSuffix, m_MsgNameColorCode); + + // Break up the individual permissions on each dot, into m_SplitPermissions: + m_SplitPermissions.clear(); + m_SplitPermissions.reserve(m_Permissions.size()); + for (AStringVector::const_iterator itr = m_Permissions.begin(), end = m_Permissions.end(); itr != end; ++itr) + { + m_SplitPermissions.push_back(StringSplit(*itr, ".")); + } // for itr - m_Permissions[] +} + + + + + void cPlayer::Detach() { super::Detach(); diff --git a/src/Entities/Player.h b/src/Entities/Player.h index d3ed1ef9d..9821cc6d9 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -13,7 +13,6 @@ -class cGroup; class cWindow; class cClientHandle; class cTeam; @@ -236,24 +235,20 @@ public: // tolua_end - typedef std::list< cGroup* > GroupList; - typedef std::list< std::string > StringList; + bool HasPermission(const AString & a_Permission); // tolua_export - /** Adds a player to existing group or creates a new group when it doesn't exist */ - void AddToGroup( const AString & a_GroupName); // tolua_export - - /** Removes a player from the group, resolves permissions and group inheritance (case sensitive) */ - void RemoveFromGroup( const AString & a_GroupName); // tolua_export - - bool HasPermission( const AString & a_Permission); // tolua_export - const GroupList & GetGroups() { return m_Groups; } // >> EXPORTED IN MANUALBINDINGS << - StringList GetResolvedPermissions(); // >> EXPORTED IN MANUALBINDINGS << - bool IsInGroup( const AString & a_Group); // tolua_export + /** Returns true iff a_Permission matches the a_Template. + A match is defined by either being exactly the same, or each sub-item matches until there's a wildcard in a_Template. + Ie. {"a", "b", "c"} matches {"a", "b", "*"} but doesn't match {"a", "b"} */ + static bool PermissionMatches(const AStringVector & a_Permission, const AStringVector & a_Template); // Exported in ManualBindings with AString params + + /** Returns all the permissions that the player has assigned to them. */ + const AStringVector & GetPermissions(void) { return m_Permissions; } // Exported in ManualBindings.cpp // tolua_begin - /** Returns the full color code to use for this player, based on their primary group or set in m_Color. - The returned value includes the cChatColor::Delimiter. */ + /** Returns the full color code to use for this player, based on their rank. + The returned value either is empty, or includes the cChatColor::Delimiter. */ AString GetColor(void) const; /** tosses the item in the selected hotbar slot */ @@ -347,8 +342,6 @@ public: */ bool LoadFromFile(const AString & a_FileName, cWorldPtr & a_World); - void LoadPermissionsFromDisk(void); // tolua_export - const AString & GetLoadedWorldName() { return m_LoadedWorldName; } void UseEquippedItem(int a_Amount = 1); @@ -422,6 +415,11 @@ public: /** Returns the UUID (short format) that has been read from the client, or empty string if not available. */ const AString & GetUUID(void) const { return m_UUID; } + /** (Re)loads the rank and permissions from the cRankManager. + Expects the m_UUID member to be valid. + Loads the m_Rank, m_Permissions, m_MsgPrefix, m_MsgSuffix and m_MsgNameColorCode members. */ + void LoadRank(void); + // tolua_end // cEntity overrides: @@ -432,12 +430,22 @@ public: virtual void Detach(void); protected: - typedef std::map< std::string, bool > PermissionMap; - PermissionMap m_ResolvedPermissions; - PermissionMap m_Permissions; - GroupList m_ResolvedGroups; - GroupList m_Groups; + typedef std::vector<std::vector<AString> > AStringVectorVector; + + /** The name of the rank assigned to this player. */ + AString m_Rank; + + /** All the permissions that this player has, based on their rank. */ + AStringVector m_Permissions; + + /** All the permissions that this player has, based on their rank, split into individual dot-delimited parts. + This is used mainly by the HasPermission() function to optimize the lookup. */ + AStringVectorVector m_SplitPermissions; + + // Message visuals: + AString m_MsgPrefix, m_MsgSuffix; + AString m_MsgNameColorCode; AString m_PlayerName; AString m_LoadedWorldName; @@ -482,8 +490,6 @@ protected: /** The player's last saved bed position */ Vector3i m_LastBedPos; - char m_Color; - eGameMode m_GameMode; AString m_IP; diff --git a/src/Group.cpp b/src/Group.cpp deleted file mode 100644 index def585618..000000000 --- a/src/Group.cpp +++ /dev/null @@ -1,41 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Group.h" - - - - - -void cGroup::AddCommand( const AString & a_Command) -{ - m_Commands[ a_Command ] = true; -} - - - - - -void cGroup::AddPermission( const AString & a_Permission) -{ - m_Permissions[ a_Permission ] = true; -} - - - - - -void cGroup::InheritFrom( cGroup* a_Group) -{ - m_Inherits.remove( a_Group); - m_Inherits.push_back( a_Group); -} - - - - - -void cGroup::ClearPermission() -{ - m_Permissions.clear(); -} diff --git a/src/Group.h b/src/Group.h deleted file mode 100644 index 5816f8a06..000000000 --- a/src/Group.h +++ /dev/null @@ -1,44 +0,0 @@ - -#pragma once - - - - - -// tolua_begin -class cGroup -{ -public: - // tolua_end - cGroup() {} - ~cGroup() {} - - // tolua_begin - void SetName( const AString & a_Name) { m_Name = a_Name; } - const AString & GetName() const { return m_Name; } - void SetColor( const AString & a_Color) { m_Color = a_Color; } - void AddCommand( const AString & a_Command); - void AddPermission( const AString & a_Permission); - void InheritFrom( cGroup* a_Group); - // tolua_end - - typedef std::map< AString, bool > PermissionMap; - const PermissionMap & GetPermissions() const { return m_Permissions; } - - void ClearPermission(void); - - typedef std::map< AString, bool > CommandMap; - const CommandMap & GetCommands() const { return m_Commands; } - - const AString & GetColor() const { return m_Color; } // tolua_export - - typedef std::list< cGroup* > GroupList; - const GroupList & GetInherits() const { return m_Inherits; } -private: - AString m_Name; - AString m_Color; - - PermissionMap m_Permissions; - CommandMap m_Commands; - GroupList m_Inherits; -}; // tolua_export diff --git a/src/GroupManager.cpp b/src/GroupManager.cpp deleted file mode 100644 index 4c3dfc6f0..000000000 --- a/src/GroupManager.cpp +++ /dev/null @@ -1,227 +0,0 @@ -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "GroupManager.h" -#include "Group.h" -#include "inifile/iniFile.h" -#include "ChatColor.h" -#include "Root.h" - - - - - -typedef std::map< AString, cGroup* > GroupMap; - - - - - -struct cGroupManager::sGroupManagerState -{ - GroupMap Groups; -}; - - - - - -cGroupManager::~cGroupManager() -{ - for (GroupMap::iterator itr = m_pState->Groups.begin(); itr != m_pState->Groups.end(); ++itr) - { - delete itr->second; - itr->second = NULL; - } - m_pState->Groups.clear(); - - delete m_pState; - m_pState = NULL; -} - - - - - -cGroupManager::cGroupManager() - : m_pState( new sGroupManagerState) -{ - LOGD("-- Loading Groups --"); - - if (!LoadGroups()) - { - LOGWARNING("ERROR: Groups could not load!"); - } - if (!CheckUsers()) - { - LOGWARNING("ERROR: User file could not be found!"); - } - - LOGD("-- Groups Successfully Loaded --"); -} - - - - - -void cGroupManager::GenerateDefaultUsersIni(cIniFile & a_IniFile) -{ - LOGWARN("Regenerating users.ini, all users will be reset"); - a_IniFile.AddHeaderComment(" This file stores the players' groups."); - a_IniFile.AddHeaderComment(" The format is:"); - a_IniFile.AddHeaderComment(" [PlayerName]"); - a_IniFile.AddHeaderComment(" Groups = GroupName1, GroupName2, ..."); - - a_IniFile.WriteFile("users.ini"); -} - - - - - -bool cGroupManager::CheckUsers() -{ - cIniFile IniFile; - if (!IniFile.ReadFile("users.ini")) - { - GenerateDefaultUsersIni(IniFile); - return true; - } - - int NumKeys = IniFile.GetNumKeys(); - for (int i = 0; i < NumKeys; i++) - { - AString Player = IniFile.GetKeyName(i); - AString Groups = IniFile.GetValue(Player, "Groups", ""); - if (Groups.empty()) - { - continue; - } - AStringVector Split = StringSplitAndTrim(Groups, ","); - for (AStringVector::const_iterator itr = Split.begin(), end = Split.end(); itr != end; ++itr) - { - if (!ExistsGroup(*itr)) - { - LOGWARNING("The group %s for player %s was not found!", Split[i].c_str(), Player.c_str()); - } - } // for itr - Split[] - } // for i - ini file keys - // Always return true for now, just but we can handle writefile fails later. - return true; -} - - - - - -bool cGroupManager::LoadGroups() -{ - cIniFile IniFile; - if (!IniFile.ReadFile("groups.ini")) - { - LOGWARNING("Regenerating groups.ini, all groups will be reset"); - IniFile.AddHeaderComment(" This is the MCServer permissions manager groups file"); - IniFile.AddHeaderComment(" It stores all defined groups such as Administrators, Players, or Moderators"); - - IniFile.SetValue("Owner", "Permissions", "*", true); - IniFile.SetValue("Owner", "Color", "2", true); - - IniFile.SetValue("Moderator", "Permissions", "core.time, core.item, core.tpa, core.tpaccept, core.ban, core.unban, core.save-all, core.toggledownfall"); - IniFile.SetValue("Moderator", "Color", "2", true); - IniFile.SetValue("Moderator", "Inherits", "Player", true); - - IniFile.SetValue("Player", "Permissions", "core.portal", true); - IniFile.SetValue("Player", "Color", "f", true); - IniFile.SetValue("Player", "Inherits", "Default", true); - - IniFile.SetValue("Default", "Permissions", "core.help, core.plugins, core.spawn, core.worlds, core.back, core.motd, core.build, core.locate, core.viewdistance", true); - IniFile.SetValue("Default", "Color", "f", true); - - IniFile.WriteFile("groups.ini"); - } - - int NumKeys = IniFile.GetNumKeys(); - for (int i = 0; i < NumKeys; i++) - { - AString KeyName = IniFile.GetKeyName(i); - cGroup * Group = GetGroup(KeyName.c_str()); - - Group->ClearPermission(); // Needed in case the groups are reloaded. - - LOGD("Loading group %s", KeyName.c_str()); - - Group->SetName(KeyName); - AString Color = IniFile.GetValue(KeyName, "Color", "-"); - if ((Color != "-") && (Color.length() >= 1)) - { - Group->SetColor(AString(cChatColor::Delimiter) + Color[0]); - } - else - { - Group->SetColor(cChatColor::White); - } - - AString Commands = IniFile.GetValue(KeyName, "Commands", ""); - if (!Commands.empty()) - { - AStringVector Split = StringSplitAndTrim(Commands, ","); - for (size_t i = 0; i < Split.size(); i++) - { - Group->AddCommand(Split[i]); - } - } - - AString Permissions = IniFile.GetValue(KeyName, "Permissions", ""); - if (!Permissions.empty()) - { - AStringVector Split = StringSplitAndTrim(Permissions, ","); - for (size_t i = 0; i < Split.size(); i++) - { - Group->AddPermission(Split[i]); - } - } - - AString Groups = IniFile.GetValue(KeyName, "Inherits", ""); - if (!Groups.empty()) - { - AStringVector Split = StringSplitAndTrim(Groups, ","); - for (size_t i = 0; i < Split.size(); i++) - { - Group->InheritFrom(GetGroup(Split[i].c_str())); - } - } - } - // Always return true, we can handle writefile fails later. - return true; -} - - - - - -bool cGroupManager::ExistsGroup( const AString & a_Name) -{ - GroupMap::iterator itr = m_pState->Groups.find( a_Name); - return ( itr != m_pState->Groups.end()); -} - - - - - -cGroup* cGroupManager::GetGroup( const AString & a_Name) -{ - GroupMap::iterator itr = m_pState->Groups.find( a_Name); - if (itr != m_pState->Groups.end()) - { - return itr->second; - } - - cGroup* Group = new cGroup(); - m_pState->Groups[a_Name] = Group; - - return Group; -} - - - - diff --git a/src/GroupManager.h b/src/GroupManager.h deleted file mode 100644 index d42b55c4a..000000000 --- a/src/GroupManager.h +++ /dev/null @@ -1,36 +0,0 @@ - -#pragma once - - - - - -class cGroup; - - - - - -class cGroupManager -{ -public: - bool ExistsGroup(const AString & a_Name); - cGroup * GetGroup(const AString & a_Name); - bool LoadGroups(); - bool CheckUsers(); - - /** Writes the default header to the specified ini file, and saves it as "users.ini". */ - static void GenerateDefaultUsersIni(cIniFile & a_IniFile); - -private: - friend class cRoot; - cGroupManager(); - ~cGroupManager(); - - struct sGroupManagerState; - sGroupManagerState * m_pState; -} ; - - - - diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp index 83c2dc300..4e5c41a8a 100644 --- a/src/Protocol/MojangAPI.cpp +++ b/src/Protocol/MojangAPI.cpp @@ -10,6 +10,7 @@ #include "inifile/iniFile.h" #include "json/json.h" #include "PolarSSL++/BlockingSslClientSocket.h" +#include "../RankManager.h" @@ -300,6 +301,7 @@ void cMojangAPI::AddPlayerNameToUUIDMapping(const AString & a_PlayerName, const cCSLock Lock(m_CSUUIDToName); m_UUIDToName[UUID] = sProfile(a_PlayerName, UUID, "", "", Now); } + NotifyNameUUID(a_PlayerName, a_UUID); } @@ -322,6 +324,7 @@ void cMojangAPI::AddPlayerProfile(const AString & a_PlayerName, const AString & cCSLock Lock(m_CSUUIDToProfile); m_UUIDToProfile[UUID] = sProfile(a_PlayerName, UUID, a_Properties, Now); } + NotifyNameUUID(a_PlayerName, a_UUID); } @@ -669,6 +672,7 @@ void cMojangAPI::CacheNamesToUUIDs(const AStringVector & a_PlayerNames) continue; } m_NameToUUID[StrToLower(JsonName)] = sProfile(JsonName, JsonUUID, "", "", Now); + NotifyNameUUID(JsonName, JsonUUID); } // for idx - root[] } // cCSLock (m_CSNameToUUID) @@ -792,6 +796,21 @@ void cMojangAPI::CacheUUIDToProfile(const AString & a_UUID) cCSLock Lock(m_CSNameToUUID); m_NameToUUID[StrToLower(PlayerName)] = sProfile(PlayerName, a_UUID, Properties, Now); } + NotifyNameUUID(PlayerName, a_UUID); +} + + + + + +void cMojangAPI::NotifyNameUUID(const AString & a_PlayerName, const AString & a_UUID) +{ + // Notify the rank manager: + cCSLock Lock(m_CSRankMgr); + if (m_RankMgr != NULL) + { + m_RankMgr->NotifyNameUUID(a_PlayerName, a_UUID); + } } diff --git a/src/Protocol/MojangAPI.h b/src/Protocol/MojangAPI.h index 6ed37625e..252d32543 100644 --- a/src/Protocol/MojangAPI.h +++ b/src/Protocol/MojangAPI.h @@ -11,6 +11,13 @@ #include <time.h> + + + + +// fwd: ../RankManager.h" +class cRankManager; + namespace Json { class Value; @@ -38,8 +45,6 @@ public: Returns true if all was successful, false on failure. */ static bool SecureRequest(const AString & a_ServerName, const AString & a_Request, AString & a_Response); - // tolua_begin - /** Normalizes the given UUID to its short form (32 bytes, no dashes, lowercase). Logs a warning and returns empty string if not a UUID. Note: only checks the string's length, not the actual content. */ @@ -50,8 +55,6 @@ public: Note: only checks the string's length, not the actual content. */ static AString MakeUUIDDashed(const AString & a_UUID); - // tolua_end - /** Converts a player name into a UUID. The UUID will be empty on error. If a_UseOnlyCached is true, the function only consults the cached values. @@ -85,7 +88,10 @@ public: /** Called by the Authenticator to add a profile that it has received from authenticating a user. Adds the profile to the respective mapping caches and updtes their datetime stamp to now. */ void AddPlayerProfile(const AString & a_PlayerName, const AString & a_UUID, const Json::Value & a_Properties); - + + /** Sets the m_RankMgr that is used for name-uuid notifications. Accepts NULL to remove the binding. */ + void SetRankManager(cRankManager * a_RankManager) { m_RankMgr = a_RankManager; } + protected: /** Holds data for a single player profile. */ struct sProfile @@ -165,6 +171,12 @@ protected: /** Protects m_UUIDToProfile against simultaneous multi-threaded access. */ cCriticalSection m_CSUUIDToProfile; + + /** The rank manager that is notified of the name-uuid pairings. May be NULL. Protected by m_CSRankMgr. */ + cRankManager * m_RankMgr; + + /** Protects m_RankMgr agains simultaneous multi-threaded access. */ + cCriticalSection m_CSRankMgr; /** Loads the caches from a disk storage. */ @@ -182,6 +194,10 @@ protected: UUIDs that are not valid will not be added into the cache. ASSUMEs that a_UUID is a lowercased short UUID. */ void CacheUUIDToProfile(const AString & a_UUID); + + /** Called for each name-uuid pairing that is discovered. + If assigned, notifies the m_RankManager of the event. */ + void NotifyNameUUID(const AString & a_PlayerName, const AString & a_PlayerUUID); } ; // tolua_export diff --git a/src/RankManager.cpp b/src/RankManager.cpp new file mode 100644 index 000000000..65ce33b92 --- /dev/null +++ b/src/RankManager.cpp @@ -0,0 +1,1839 @@ + +// RankManager.cpp + +// Implements the cRankManager class that represents the rank manager responsible for assigning permissions and message visuals to players + +#include "Globals.h" +#include "RankManager.h" +#include "inifile/iniFile.h" +#include "Protocol/MojangAPI.h" +#include "ClientHandle.h" + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cRankManagerIniMigrator: + +/** Migrates from groups.ini and users.ini into the rankmanager DB */ +class cRankManagerIniMigrator +{ +public: + cRankManagerIniMigrator(cRankManager & a_RankManager, cMojangAPI & a_MojangAPI) : + m_RankManager(a_RankManager), + m_MojangAPI(a_MojangAPI) + { + } + + + + /** Performs the complete migration from INI files to DB. */ + bool Migrate(void) + { + cRankManager::cMassChangeLock Lock(m_RankManager); + + LOGD("Reading groups..."); + if (!ReadGroups()) + { + return false; + } + LOGD("Cleaning groups inheritance..."); + CleanGroupInheritance(); + LOGD("Creating groups..."); + CreateGroups(); + + LOGD("Reading users..."); + if (!ReadUsers()) + { + return false; + } + LOGD("Cleaning user groups..."); + CleanUserGroups(); + LOGD("Resolving user UUIDs..."); + ResolveUserUUIDs(); + + LOGD("Setting ranks..."); + SetRanks(); + + LOGD("Creating defaults..."); + CreateDefaults(); + + return true; + } + +protected: + + /** Container for a group read from an INI file. */ + struct sGroup + { + AString m_Name; + AString m_Color; + AStringVector m_Inherits; + AStringVector m_Permissions; + + sGroup(void) {} + + sGroup(const AString & a_Name, const AString & a_Color, const AStringVector & a_Inherits, const AStringVector & a_Permissions): + m_Name(a_Name), + m_Color(a_Color), + m_Inherits(a_Inherits), + m_Permissions(a_Permissions) + { + } + }; + typedef std::map<AString, sGroup> sGroupMap; + + + /** Container for a single user read from an INI file. */ + struct sUser + { + AString m_Name; + AStringVector m_Groups; + + /** Assigned by ResolveUserUUIDs(), contains the online (Mojang) UUID of the player. */ + AString m_UUID; + + /** Assigned by ResolveUserUUIDs(), contains the offline (generated) UUID of the player. */ + AString m_OfflineUUID; + + + sUser(void) {} + + sUser(const AString & a_Name, const AStringVector & a_Groups): + m_Name(a_Name), + m_Groups(a_Groups) + { + } + }; + typedef std::map<AString, sUser> sUserMap; + + typedef std::map<AString, AString> cStringMap; + + + /** The parent Rank manager where we will create the groups, ranks and players */ + cRankManager & m_RankManager; + + /** The player name to UUID resolver */ + cMojangAPI & m_MojangAPI; + + /** List of all groups read from the ini file */ + sGroupMap m_Groups; + + /** List of all players read from the ini file. */ + sUserMap m_Users; + + /** Maps lists of groups to rank names. + Each group list is either a simple "<Group>" if there's only one group, + or "<PrimaryGroup>,<FirstSecondaryGroup>,<SecondSecondaryGroup>...", where the secondary groups are + lowercased and alpha-sorted. This makes the group lists comparable for equivalence, simply by comparing + their string names. + The ranks are named "<Group>" for single-group players, and "AutoMigratedRank_N" for the composite ranks, + where N is a unique number. */ + cStringMap m_GroupsToRanks; + + + + /** Reads the groups from the "groups.ini" file into m_Groups */ + bool ReadGroups(void) + { + // Read the file: + cIniFile Groups; + if (!Groups.ReadFile("groups.ini")) + { + return false; + } + + // Read all the groups into a map: + int NumGroups = Groups.GetNumKeys(); + for (int i = 0; i < NumGroups; i++) + { + AString GroupName = Groups.GetKeyName(i); + AString lcGroupName = StrToLower(GroupName); + if (m_Groups.find(lcGroupName) != m_Groups.end()) + { + LOGINFO("groups.ini contains a duplicate definition of group %s, ignoring the latter.", GroupName.c_str()); + continue; + } + m_Groups[lcGroupName] = sGroup( + GroupName, + Groups.GetValue(GroupName, "Color", ""), + StringSplitAndTrim(Groups.GetValue(GroupName, "Inherits"), ","), + StringSplitAndTrim(Groups.GetValue(GroupName, "Permissions"), ",") + ); + } // for i - Groups' keys + return true; + } + + + + /** Removes non-existent groups from all the groups' inheritance */ + void CleanGroupInheritance(void) + { + for (sGroupMap::iterator itrG = m_Groups.begin(), endG = m_Groups.end(); itrG != endG; ++itrG) + { + AStringVector & Inherits = itrG->second.m_Inherits; + for (AStringVector::iterator itrI = Inherits.begin(); itrI != Inherits.end();) + { + AString lcInherits = StrToLower(*itrI); + if (m_Groups.find(lcInherits) != m_Groups.end()) + { + // Inherited group exists, continue checking the next one + ++itrI; + continue; + } + // Inherited group doesn't exist, remove it from the list: + LOGWARNING("RankMigrator: Group \"%s\" inherits from a non-existent group \"%s\", this inheritance will be ignored.", + itrG->second.m_Name.c_str(), itrI->c_str() + ); + AStringVector::iterator itrI2 = itrI; + ++itrI2; + Inherits.erase(itrI); + itrI = itrI2; + } // for itrI - Inherits[] + } // for itrG - m_Groups[] + } + + + + /** Reads the users from the "users.ini" file into m_Users */ + bool ReadUsers(void) + { + // Read the file: + cIniFile Users; + if (!Users.ReadFile("users.ini")) + { + return false; + } + + // Read all the users into a map: + int NumUsers = Users.GetNumKeys(); + for (int i = 0; i < NumUsers; i++) + { + AString UserName = Users.GetKeyName(i); + AString lcUserName = StrToLower(UserName); + if (m_Users.find(lcUserName) != m_Users.end()) + { + LOGINFO("users.ini contains a duplicate definition of user %s, ignoring the latter.", UserName.c_str()); + continue; + } + m_Users[lcUserName] = sUser( + UserName, + StringSplitAndTrim(Users.GetValue(UserName, "Groups", ""), ",") + ); + } // for i - Users' keys + return true; + } + + + + /** Removes non-existent groups from each user's definition. */ + void CleanUserGroups(void) + { + for (sUserMap::iterator itrU = m_Users.begin(), endU = m_Users.end(); itrU != endU; ++itrU) + { + AStringVector & Groups = itrU->second.m_Groups; + for (AStringVector::iterator itrG = Groups.begin(); itrG != Groups.end();) + { + AString lcGroup = StrToLower(*itrG); + if (m_Groups.find(lcGroup) != m_Groups.end()) + { + // Assigned group exists, continue checking the next one + ++itrG; + continue; + } + // Assigned group doesn't exist, remove it from the list: + LOGWARNING("RankMigrator: User \"%s\" is assigned a non-existent group \"%s\", this assignment will be ignored.", + itrU->second.m_Name.c_str(), itrG->c_str() + ); + AStringVector::iterator itrG2 = itrG; + ++itrG2; + Groups.erase(itrG); + itrG = itrG2; + } // for itrG - Groups[] + } // for itrU - m_Users[] + } + + + + /** Creates groups based on m_Groups. + Ignores group inheritance. */ + void CreateGroups(void) + { + // Create each group, with its permissions: + for (sGroupMap::const_iterator itr = m_Groups.begin(), end = m_Groups.end(); itr != end; ++itr) + { + m_RankManager.AddGroup(itr->second.m_Name); + m_RankManager.AddPermissionsToGroup(itr->second.m_Permissions, itr->second.m_Name); + } // for itr - m_Groups[] + } + + + /** Resolves the UUID of each user in m_Users. + If a user doesn't resolve, they are removed and logged in the console. */ + void ResolveUserUUIDs(void) + { + // Resolve all PlayerNames at once (the API doesn't like single-name queries): + AStringVector PlayerNames; + for (sUserMap::const_iterator itr = m_Users.begin(), end = m_Users.end(); itr != end; ++itr) + { + PlayerNames.push_back(itr->second.m_Name); + } + m_MojangAPI.GetUUIDsFromPlayerNames(PlayerNames); + + // Assign the UUIDs back to players, remove those not resolved: + for (sUserMap::iterator itr = m_Users.begin(); itr != m_Users.end(); ++itr) + { + AString UUID = m_MojangAPI.GetUUIDFromPlayerName(itr->second.m_Name); + if (UUID.empty()) + { + LOGWARNING("RankMigrator: Cannot resolve player %s to online UUID, player will be left unranked in online mode", itr->second.m_Name.c_str()); + } + itr->second.m_UUID = UUID; + itr->second.m_OfflineUUID = cClientHandle::GenerateOfflineUUID(itr->second.m_Name); + } + } + + + + /** Adds the specified groups to the specified ranks. Recurses on the groups' inheritance. */ + void AddGroupsToRank(const AStringVector & a_Groups, const AString & a_RankName) + { + for (AStringVector::const_iterator itr = a_Groups.begin(), end = a_Groups.end(); itr != end; ++itr) + { + // Normalize the group name: + sGroup & Group = m_Groups[StrToLower(*itr)]; + + // Avoid loops, check if the group is already added: + if (m_RankManager.IsGroupInRank(Group.m_Name, a_RankName)) + { + continue; + } + + // Add the group, and all the groups it inherits from recursively: + m_RankManager.AddGroupToRank(Group.m_Name, a_RankName); + AddGroupsToRank(Group.m_Inherits, a_RankName); + } // for itr - a_Groups[] + } + + + + /** Creates a rank for each player, based on the master groups they are assigned. */ + void SetRanks(void) + { + for (sUserMap::const_iterator itr = m_Users.begin(), end = m_Users.end(); itr != end; ++itr) + { + // Ignore users with no groups: + const AStringVector & Groups = itr->second.m_Groups; + if (Groups.empty()) + { + LOGWARNING("RankMigrator: Player %s has no groups assigned to them, skipping the player.", itr->second.m_Name.c_str()); + continue; + } + + // Compose the rank name out of group names: + AString RankName; + for (AStringVector::const_iterator itrG = Groups.begin(), endG = Groups.end(); itrG != endG; ++itrG) + { + AString GroupName = m_Groups[StrToLower(*itrG)].m_Name; // Normalize group name + if (!RankName.empty()) + { + RankName.push_back(','); + } + RankName.append(GroupName); + } // for itrG - Groups[] + + // Create the rank, with al its groups: + if (!m_RankManager.RankExists(RankName)) + { + m_RankManager.AddRank(RankName, "", "", m_Groups[StrToLower(Groups[0])].m_Color); + AddGroupsToRank(Groups, RankName); + } + + // Set the rank to the user, using both the online and offline UUIDs: + m_RankManager.SetPlayerRank(itr->second.m_UUID, itr->second.m_Name, RankName); + m_RankManager.SetPlayerRank(itr->second.m_OfflineUUID, itr->second.m_Name, RankName); + } // for itr - m_Users[] + } + + + + /** Creates the Default rank that contains the Default group, if it exists. + Sets the RankManager's default rank. */ + void CreateDefaults(void) + { + if (!m_RankManager.RankExists("Default")) + { + m_RankManager.AddRank("Default", "", "", ""); + if (!m_RankManager.IsGroupInRank("Default", "Default")) + { + m_RankManager.AddGroupToRank("Default", "Default"); + } + } + m_RankManager.SetDefaultRank("Default"); + } +}; + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cRankManager: + +cRankManager::cRankManager(void) : + m_DB("Ranks.sqlite", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE), + m_IsInitialized(false), + m_MojangAPI(NULL) +{ +} + + + + + +cRankManager::~cRankManager() +{ + if (m_MojangAPI != NULL) + { + m_MojangAPI->SetRankManager(NULL); + } +} + + + + + +void cRankManager::Initialize(cMojangAPI & a_MojangAPI) +{ + ASSERT(!m_IsInitialized); // Calling Initialize for the second time? + + // Create the DB tables, if they don't exist: + m_DB.exec("CREATE TABLE IF NOT EXISTS Rank (RankID INTEGER PRIMARY KEY, Name, MsgPrefix, MsgSuffix, MsgNameColorCode)"); + m_DB.exec("CREATE TABLE IF NOT EXISTS PlayerRank (PlayerUUID, PlayerName, RankID INTEGER)"); + m_DB.exec("CREATE TABLE IF NOT EXISTS PermGroup (PermGroupID INTEGER PRIMARY KEY, Name)"); + m_DB.exec("CREATE TABLE IF NOT EXISTS RankPermGroup (RankID INTEGER, PermGroupID INTEGER)"); + m_DB.exec("CREATE TABLE IF NOT EXISTS PermissionItem (PermGroupID INTEGER, Permission)"); + m_DB.exec("CREATE TABLE IF NOT EXISTS DefaultRank (RankID INTEGER)"); + + m_IsInitialized = true; + + a_MojangAPI.SetRankManager(this); + + // Check if tables empty, migrate from ini files then + if (AreDBTablesEmpty()) + { + LOGINFO("There are no ranks, migrating old-style INI files to new DB ranks..."); + LOGINFO("(This might take a while)"); + cRankManagerIniMigrator Migrator(*this, a_MojangAPI); + if (Migrator.Migrate()) + { + LOGINFO("Ranks migrated."); + // The default rank has been set by the migrator + return; + } + + // Migration failed. Add some defaults + LOGINFO("Rank migration failed, creating default ranks..."); + CreateDefaults(); + LOGINFO("Default ranks created."); + } + + // Load the default rank: + try + { + SQLite::Statement stmt(m_DB, + "SELECT Rank.Name FROM Rank " + "LEFT JOIN DefaultRank ON Rank.RankID = DefaultRank.RankID" + ); + if (stmt.executeStep()) + { + m_DefaultRank = stmt.getColumn(0).getText(); + } + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Cannot load default rank: %s", __FUNCTION__, ex.what()); + return; + } + + // If the default rank cannot be loaded, use the first rank: + if (m_DefaultRank.empty()) + { + SetDefaultRank(GetAllRanks()[0]); + } +} + + + + + +AString cRankManager::GetPlayerRankName(const AString & a_PlayerUUID) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + SQLite::Statement stmt(m_DB, "SELECT Rank.Name FROM Rank LEFT JOIN PlayerRank ON Rank.RankID = PlayerRank.RankID WHERE PlayerRank.PlayerUUID = ?"); + stmt.bind(1, a_PlayerUUID); + stmt.executeStep(); + if (stmt.isDone()) + { + // No data returned from the DB + return AString(); + } + return stmt.getColumn(0).getText(); + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Cannot get player rank name: %s", __FUNCTION__, ex.what()); + } + return AString(); +} + + + + + +AStringVector cRankManager::GetPlayerGroups(const AString & a_PlayerUUID) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + AStringVector res; + try + { + // Prepare the DB statement: + SQLite::Statement stmt(m_DB, + "SELECT PermGroup.Name FROM PermGroup " + "LEFT JOIN RankPermGroup " + "ON PermGroup.PermGroupID = RankPermGroup.PermGroupID " + "LEFT JOIN PlayerRank " + "ON PlayerRank.RankID = RankPermGroup.RankID " + "WHERE PlayerRank.PlayerUUID = ?" + ); + stmt.bind(1, a_PlayerUUID); + + // Execute and get results: + while (stmt.executeStep()) + { + res.push_back(stmt.getColumn(0).getText()); + } + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Cannot get player groups: %s", __FUNCTION__, ex.what()); + } + return res; +} + + + + + +AStringVector cRankManager::GetPlayerPermissions(const AString & a_PlayerUUID) +{ + AString Rank = GetPlayerRankName(a_PlayerUUID); + if (Rank.empty()) + { + Rank = m_DefaultRank; + } + return GetRankPermissions(Rank); +} + + + + + +AStringVector cRankManager::GetRankGroups(const AString & a_RankName) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + AStringVector res; + try + { + SQLite::Statement stmt(m_DB, + "SELECT PermGroup.Name FROM PermGroup " + "LEFT JOIN RankPermGroup ON RankPermGroup.PermGroupID = PermGroup.PermGroupID " + "LEFT JOIN Rank ON Rank.RankID = RankPermGroup.RankID " + "WHERE Rank.Name = ?" + ); + stmt.bind(1, a_RankName); + while (stmt.executeStep()) + { + res.push_back(stmt.getColumn(0).getText()); + } + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to get rank groups from DB: %s", __FUNCTION__, ex.what()); + } + return res; +} + + + + + +AStringVector cRankManager::GetGroupPermissions(const AString & a_GroupName) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + AStringVector res; + try + { + SQLite::Statement stmt(m_DB, + "SELECT PermissionItem.Permission FROM PermissionItem " + "LEFT JOIN PermGroup ON PermGroup.PermGroupID = PermissionItem.PermGroupID " + "WHERE PermGroup.Name = ?" + ); + stmt.bind(1, a_GroupName); + while (stmt.executeStep()) + { + res.push_back(stmt.getColumn(0).getText()); + } + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to get group permissions from DB: %s", __FUNCTION__, ex.what()); + } + return res; +} + + + + + +AStringVector cRankManager::GetRankPermissions(const AString & a_RankName) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + AStringVector res; + try + { + SQLite::Statement stmt(m_DB, + "SELECT PermissionItem.Permission FROM PermissionItem " + "LEFT JOIN RankPermGroup ON RankPermGroup.PermGroupID = PermissionItem.PermGroupID " + "LEFT JOIN Rank ON Rank.RankID = RankPermGroup.RankID " + "WHERE Rank.Name = ?" + ); + stmt.bind(1, a_RankName); + while (stmt.executeStep()) + { + res.push_back(stmt.getColumn(0).getText()); + } + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to get rank permissions from DB: %s", __FUNCTION__, ex.what()); + } + return res; +} + + + + + +AStringVector cRankManager::GetAllRanks(void) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + AStringVector res; + try + { + SQLite::Statement stmt(m_DB, "SELECT Name FROM Rank"); + while (stmt.executeStep()) + { + res.push_back(stmt.getColumn(0).getText()); + } + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to get ranks from DB: %s", __FUNCTION__, ex.what()); + } + return res; +} + + + + + +AStringVector cRankManager::GetAllGroups(void) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + AStringVector res; + try + { + SQLite::Statement stmt(m_DB, "SELECT Name FROM PermGroup"); + while (stmt.executeStep()) + { + res.push_back(stmt.getColumn(0).getText()); + } + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to get groups from DB: %s", __FUNCTION__, ex.what()); + } + return res; +} + + + + + +AStringVector cRankManager::GetAllPermissions(void) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + AStringVector res; + try + { + SQLite::Statement stmt(m_DB, "SELECT DISTINCT(Permission) FROM PermissionItem"); + while (stmt.executeStep()) + { + res.push_back(stmt.getColumn(0).getText()); + } + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to get permissions from DB: %s", __FUNCTION__, ex.what()); + } + return res; +} + + + + + +bool cRankManager::GetPlayerMsgVisuals( + const AString & a_PlayerUUID, + AString & a_MsgPrefix, + AString & a_MsgSuffix, + AString & a_MsgNameColorCode +) +{ + AString Rank = GetPlayerRankName(a_PlayerUUID); + if (Rank.empty()) + { + // Rank not found, return failure: + a_MsgPrefix.clear(); + a_MsgSuffix.clear(); + a_MsgNameColorCode.clear(); + return false; + } + return GetRankVisuals(Rank, a_MsgPrefix, a_MsgSuffix, a_MsgNameColorCode); +} + + + + + +void cRankManager::AddRank( + const AString & a_RankName, + const AString & a_MsgPrefix, + const AString & a_MsgSuffix, + const AString & a_MsgNameColorCode +) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + // Check if such a rank name is already used: + { + SQLite::Statement stmt(m_DB, "SELECT COUNT(*) FROM Rank WHERE Name = ?"); + stmt.bind(1, a_RankName); + if (stmt.executeStep()) + { + if (stmt.getColumn(0).getInt() > 0) + { + // Rank already exists, do nothing: + return; + } + } + } + + // Insert a new rank: + SQLite::Statement stmt(m_DB, "INSERT INTO Rank (Name, MsgPrefix, MsgSuffix, MsgNameColorCode) VALUES (?, ?, ?, ?)"); + stmt.bind(1, a_RankName); + stmt.bind(2, a_MsgPrefix); + stmt.bind(3, a_MsgSuffix); + stmt.bind(4, a_MsgNameColorCode); + if (stmt.exec() <= 0) + { + LOGWARNING("%s: Failed to add a new rank \"%s\".", __FUNCTION__, a_RankName.c_str()); + return; + } + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to add a new rank \"%s\": %s", __FUNCTION__, a_RankName.c_str(), ex.what()); + } +} + + + + + +void cRankManager::AddGroup(const AString & a_GroupName) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + // Check if such a group name is already used: + { + SQLite::Statement stmt(m_DB, "SELECT COUNT(*) FROM PermGroup WHERE Name = ?"); + stmt.bind(1, a_GroupName); + if (stmt.executeStep()) + { + if (stmt.getColumn(0).getInt() > 0) + { + // Group already exists, do nothing: + return; + } + } + } + + // Insert a new group: + SQLite::Statement stmt(m_DB, "INSERT INTO PermGroup (Name) VALUES (?)"); + stmt.bind(1, a_GroupName); + if (stmt.exec() <= 0) + { + LOGWARNING("%s: Failed to add a new group \"%s\".", __FUNCTION__, a_GroupName.c_str()); + return; + } + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to add a new group \"%s\": %s", __FUNCTION__, a_GroupName.c_str(), ex.what()); + } +} + + + + + +void cRankManager::AddGroups(const AStringVector & a_GroupNames) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + for (AStringVector::const_iterator itr = a_GroupNames.begin(), end = a_GroupNames.end(); itr != end; ++itr) + { + // Check if such the group name is already used: + { + SQLite::Statement stmt(m_DB, "SELECT COUNT(*) FROM PermGroup WHERE Name = ?"); + stmt.bind(1, *itr); + if (stmt.executeStep()) + { + if (stmt.getColumn(0).getInt() > 0) + { + // Group already exists, do nothing: + return; + } + } + } + + // Insert a new group: + SQLite::Statement stmt(m_DB, "INSERT INTO PermGroup (Name) VALUES (?)"); + stmt.bind(1, *itr); + if (stmt.exec() <= 0) + { + LOGWARNING("%s: Failed to add a new group \"%s\".", __FUNCTION__, itr->c_str()); + return; + } + } // for itr - a_GroupNames[] + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to add new groups: %s", __FUNCTION__, ex.what()); + } +} + + + + + +bool cRankManager::AddGroupToRank(const AString & a_GroupName, const AString & a_RankName) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + // Get the group's ID: + int GroupID; + { + SQLite::Statement stmt(m_DB, "SELECT PermGroupID FROM PermGroup WHERE Name = ?"); + stmt.bind(1, a_GroupName); + if (!stmt.executeStep()) + { + LOGWARNING("%s: No such group (%s), aborting.", __FUNCTION__, a_GroupName.c_str()); + return false; + } + GroupID = stmt.getColumn(0); + } + + // Get the rank's ID: + int RankID; + { + SQLite::Statement stmt(m_DB, "SELECT RankID FROM Rank WHERE Name = ?"); + stmt.bind(1, a_RankName); + if (!stmt.executeStep()) + { + LOGWARNING("%s: No such rank (%s), aborting.", __FUNCTION__, a_RankName.c_str()); + return false; + } + RankID = stmt.getColumn(0); + } + + // Check if the group is already there: + { + SQLite::Statement stmt(m_DB, "SELECT COUNT(*) FROM RankPermGroup WHERE RankID = ? AND PermGroupID = ?"); + stmt.bind(1, RankID); + stmt.bind(2, GroupID); + if (!stmt.executeStep()) + { + LOGWARNING("%s: Failed to check binding between rank %s and group %s, aborting.", __FUNCTION__, a_RankName.c_str(), a_GroupName.c_str()); + return false; + } + if (stmt.getColumn(0).getInt() > 0) + { + LOGD("%s: Group %s already present in rank %s, skipping and returning success.", + __FUNCTION__, a_GroupName.c_str(), a_RankName.c_str() + ); + return true; + } + } + + // Add the group: + { + SQLite::Statement stmt(m_DB, "INSERT INTO RankPermGroup (RankID, PermGroupID) VALUES (?, ?)"); + stmt.bind(1, RankID); + stmt.bind(2, GroupID); + if (stmt.exec() <= 0) + { + LOGWARNING("%s: Failed to add group %s to rank %s, aborting.", __FUNCTION__, a_GroupName.c_str(), a_RankName.c_str()); + return false; + } + } + + // Adding succeeded: + return true; + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to add group %s to rank %s: %s", __FUNCTION__, a_GroupName.c_str(), a_RankName.c_str(), ex.what()); + } + return false; +} + + + + + +bool cRankManager::AddPermissionToGroup(const AString & a_Permission, const AString & a_GroupName) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + // Get the group's ID: + int GroupID; + { + SQLite::Statement stmt(m_DB, "SELECT PermGroupID FROM PermGroup WHERE Name = ?"); + stmt.bind(1, a_GroupName); + if (!stmt.executeStep()) + { + LOGWARNING("%s: No such group (%s), aborting.", __FUNCTION__, a_GroupName.c_str()); + return false; + } + GroupID = stmt.getColumn(0).getInt(); + } + + // Check if the permission is already present: + { + SQLite::Statement stmt(m_DB, "SELECT COUNT(*) FROM PermissionItem WHERE PermGroupID = ? AND Permission = ?"); + stmt.bind(1, GroupID); + stmt.bind(2, a_Permission); + if (!stmt.executeStep()) + { + LOGWARNING("%s: Failed to check binding between permission %s and group %s, aborting.", __FUNCTION__, a_Permission.c_str(), a_GroupName.c_str()); + return false; + } + if (stmt.getColumn(0).getInt() > 0) + { + LOGD("%s: Permission %s is already present in group %s, skipping and returning success.", + __FUNCTION__, a_Permission.c_str(), a_GroupName.c_str() + ); + return true; + } + } + + // Add the permission: + { + SQLite::Statement stmt(m_DB, "INSERT INTO PermissionItem (Permission, PermGroupID) VALUES (?, ?)"); + stmt.bind(1, a_Permission); + stmt.bind(2, GroupID); + if (stmt.exec() <= 0) + { + LOGWARNING("%s: Failed to add permission %s to group %s, aborting.", __FUNCTION__, a_Permission.c_str(), a_GroupName.c_str()); + return false; + } + } + + // Adding succeeded: + return true; + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to add permission %s to group %s: %s", + __FUNCTION__, a_Permission.c_str(), a_GroupName.c_str(), ex.what() + ); + } + return false; +} + + + + + +bool cRankManager::AddPermissionsToGroup(const AStringVector & a_Permissions, const AString & a_GroupName) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + // Get the group's ID: + int GroupID; + { + SQLite::Statement stmt(m_DB, "SELECT PermGroupID FROM PermGroup WHERE Name = ?"); + stmt.bind(1, a_GroupName); + if (!stmt.executeStep()) + { + LOGWARNING("%s: No such group (%s), aborting.", __FUNCTION__, a_GroupName.c_str()); + return false; + } + GroupID = stmt.getColumn(0).getInt(); + } + + for (AStringVector::const_iterator itr = a_Permissions.begin(), end = a_Permissions.end(); itr != end; ++itr) + { + // Check if the permission is already present: + { + SQLite::Statement stmt(m_DB, "SELECT COUNT(*) FROM PermissionItem WHERE PermGroupID = ? AND Permission = ?"); + stmt.bind(1, GroupID); + stmt.bind(2, *itr); + if (!stmt.executeStep()) + { + LOGWARNING("%s: Failed to check binding between permission %s and group %s, aborting.", __FUNCTION__, itr->c_str(), a_GroupName.c_str()); + return false; + } + if (stmt.getColumn(0).getInt() > 0) + { + LOGD("%s: Permission %s is already present in group %s, skipping and returning success.", + __FUNCTION__, itr->c_str(), a_GroupName.c_str() + ); + continue; + } + } + + // Add the permission: + { + SQLite::Statement stmt(m_DB, "INSERT INTO PermissionItem (Permission, PermGroupID) VALUES (?, ?)"); + stmt.bind(1, *itr); + stmt.bind(2, GroupID); + if (stmt.exec() <= 0) + { + LOGWARNING("%s: Failed to add permission %s to group %s, skipping.", __FUNCTION__, itr->c_str(), a_GroupName.c_str()); + continue; + } + } + } // for itr - a_Permissions[] + + // Adding succeeded: + return true; + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to add permissions to group %s: %s", + __FUNCTION__, a_GroupName.c_str(), ex.what() + ); + } + return false; +} + + + + + +void cRankManager::RemoveRank(const AString & a_RankName, const AString & a_ReplacementRankName) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + // Check if the default rank is being removed with a proper replacement: + if ((a_RankName == m_DefaultRank) && !RankExists(a_ReplacementRankName)) + { + LOGWARNING("%s: Cannot remove rank %s, it is the default rank and the replacement rank doesn't exist.", __FUNCTION__, a_RankName.c_str()); + return; + } + + AStringVector res; + try + { + // Get the RankID for the rank being removed: + int RemoveRankID; + { + SQLite::Statement stmt(m_DB, "SELECT RankID FROM Rank WHERE Name = ?"); + stmt.bind(1, a_RankName); + if (!stmt.executeStep()) + { + LOGINFO("%s: Rank %s was not found. Skipping.", __FUNCTION__, a_RankName.c_str()); + return; + } + RemoveRankID = stmt.getColumn(0).getInt(); + } + + // Get the RankID for the replacement rank: + int ReplacementRankID = -1; + { + SQLite::Statement stmt(m_DB, "SELECT RankID FROM Rank WHERE Name = ?"); + stmt.bind(1, a_ReplacementRankName); + if (stmt.executeStep()) + { + ReplacementRankID = stmt.getColumn(0).getInt(); + } + } + + // Remove the rank's bindings to groups: + { + SQLite::Statement stmt(m_DB, "DELETE FROM RankPermGroup WHERE RankID = ?"); + stmt.bind(1, RemoveRankID); + stmt.exec(); + } + + // Adjust players: + if (ReplacementRankID == -1) + { + // No replacement, just delete all the players that have the rank: + SQLite::Statement stmt(m_DB, "DELETE FROM PlayerRank WHERE RankID = ?"); + stmt.bind(1, RemoveRankID); + stmt.exec(); + } + else + { + // Replacement available, change all the player records: + SQLite::Statement stmt(m_DB, "UPDATE PlayerRank SET RankID = ? WHERE RankID = ?"); + stmt.bind(1, ReplacementRankID); + stmt.bind(2, RemoveRankID); + stmt.exec(); + } + + // Remove the rank from the DB: + { + SQLite::Statement stmt(m_DB, "DELETE FROM Rank WHERE RankID = ?"); + stmt.bind(1, RemoveRankID); + stmt.exec(); + } + + // Update the default rank, if it was the one being removed: + if (a_RankName == m_DefaultRank) + { + m_DefaultRank = a_RankName; + } + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to remove rank from DB: %s", __FUNCTION__, ex.what()); + } +} + + + + + +void cRankManager::RemoveGroup(const AString & a_GroupName) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + // Get the ID of the group: + int GroupID; + { + SQLite::Statement stmt(m_DB, "SELECT PermGroupID FROM PermGroup WHERE Name = ?"); + stmt.bind(1, a_GroupName); + if (!stmt.executeStep()) + { + LOGINFO("%s: Group %s was not found, skipping.", __FUNCTION__, a_GroupName.c_str()); + return; + } + GroupID = stmt.getColumn(0).getInt(); + } + + // Remove all permissions from the group: + { + SQLite::Statement stmt(m_DB, "DELETE FROM PermissionItem WHERE PermGroupID = ?"); + stmt.bind(1, GroupID); + stmt.exec(); + } + + // Remove the group from all ranks that contain it: + { + SQLite::Statement stmt(m_DB, "DELETE FROM RankPermGroup WHERE PermGroupID = ?"); + stmt.bind(1, GroupID); + stmt.exec(); + } + + // Remove the group itself: + { + SQLite::Statement stmt(m_DB, "DELETE FROM PermGroup WHERE PermGroupID = ?"); + stmt.bind(1, GroupID); + stmt.exec(); + } + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to remove group %s from DB: %s", __FUNCTION__, a_GroupName.c_str(), ex.what()); + } +} + + + + + +void cRankManager::RemoveGroupFromRank(const AString & a_GroupName, const AString & a_RankName) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + // Get the IDs of the group and the rank: + int GroupID, RankID; + { + SQLite::Statement stmt(m_DB, + "SELECT PermGroup.PermGroupID, Rank.RankID FROM PermGroup " + "LEFT JOIN RankPermGroup ON RankPermGroup.PermGroupID = PermGroup.PermGroupID " + "LEFT JOIN Rank ON Rank.RankID = RankPermGroup.RankID " + "WHERE PermGroup.Name = ? AND Rank.Name = ?" + ); + stmt.bind(1, a_GroupName); + stmt.bind(2, a_RankName); + if (!stmt.executeStep()) + { + LOGINFO("%s: Group %s was not found in rank %s, skipping.", __FUNCTION__, a_GroupName.c_str(), a_RankName.c_str()); + return; + } + GroupID = stmt.getColumn(0).getInt(); + RankID = stmt.getColumn(1).getInt(); + } + + // Remove the group from all ranks that contain it: + { + SQLite::Statement stmt(m_DB, "DELETE FROM RankPermGroup WHERE PermGroupID = ?"); + stmt.bind(1, GroupID); + stmt.exec(); + } + + // Remove the group-to-rank binding: + { + SQLite::Statement stmt(m_DB, "DELETE FROM RankPermGroup WHERE PermGroupID = ? AND RankID = ?"); + stmt.bind(1, GroupID); + stmt.bind(1, RankID); + stmt.exec(); + } + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to remove group %s from rank %s in the DB: %s", __FUNCTION__, a_GroupName.c_str(), a_RankName.c_str(), ex.what()); + } +} + + + + + +void cRankManager::RemovePermissionFromGroup(const AString & a_Permission, const AString & a_GroupName) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + // Get the ID of the group: + int GroupID; + { + SQLite::Statement stmt(m_DB, "SELECT PermGroupID FROM PermGroup WHERE Name = ?"); + stmt.bind(1, a_GroupName); + if (!stmt.executeStep()) + { + LOGINFO("%s: Group %s was not found, skipping.", __FUNCTION__, a_GroupName.c_str()); + return; + } + GroupID = stmt.getColumn(0).getInt(); + } + + // Remove the permission from the group: + { + SQLite::Statement stmt(m_DB, "DELETE FROM PermissionItem WHERE PermGroupID = ? AND Permission = ?"); + stmt.bind(1, GroupID); + stmt.bind(2, a_Permission); + stmt.exec(); + } + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to remove permission %s from group %s in DB: %s", + __FUNCTION__, a_Permission.c_str(), a_GroupName.c_str(), ex.what() + ); + } +} + + + + + +bool cRankManager::RenameRank(const AString & a_OldName, const AString & a_NewName) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + // Check that NewName doesn't exist: + { + SQLite::Statement stmt(m_DB, "SELECT RankID FROM Rank WHERE Name = ?"); + stmt.bind(1, a_NewName); + if (stmt.executeStep()) + { + LOGINFO("%s: Rank %s is already present, cannot rename %s", __FUNCTION__, a_NewName.c_str(), a_OldName.c_str()); + return false; + } + } + + // Rename: + { + SQLite::Statement stmt(m_DB, "UPDATE Rank SET Name = ? WHERE Name = ?"); + stmt.bind(1, a_NewName); + stmt.bind(2, a_OldName); + if (stmt.exec() <= 0) + { + LOGINFO("%s: There is no rank %s, cannot rename to %s.", __FUNCTION__, a_OldName.c_str(), a_NewName.c_str()); + return false; + } + } + + // Update the default rank, if it was the one being renamed: + if (a_OldName == m_DefaultRank) + { + m_DefaultRank = a_NewName; + } + + return true; + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to rename rank %s to %s in DB: %s", + __FUNCTION__, a_OldName.c_str(), a_NewName.c_str(), ex.what()); + } + return false; +} + + + + + +bool cRankManager::RenameGroup(const AString & a_OldName, const AString & a_NewName) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + // Check that NewName doesn't exist: + { + SQLite::Statement stmt(m_DB, "SELECT PermGroupID FROM PermGroup WHERE Name = ?"); + stmt.bind(1, a_NewName); + if (stmt.executeStep()) + { + LOGD("%s: Group %s is already present, cannot rename %s", __FUNCTION__, a_NewName.c_str(), a_OldName.c_str()); + return false; + } + } + + // Rename: + bool res; + { + SQLite::Statement stmt(m_DB, "UPDATE PermGroup SET Name = ? WHERE Name = ?"); + stmt.bind(1, a_NewName); + stmt.bind(2, a_OldName); + res = (stmt.exec() > 0); + } + + return res; + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to rename group %s to %s in DB: %s", + __FUNCTION__, a_OldName.c_str(), a_NewName.c_str(), ex.what()); + } + return false; +} + + + + + +void cRankManager::SetPlayerRank(const AString & a_PlayerUUID, const AString & a_PlayerName, const AString & a_RankName) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + // Get the rank ID: + int RankID; + { + SQLite::Statement stmt(m_DB, "SELECT RankID FROM Rank WHERE Name = ?"); + stmt.bind(1, a_RankName); + if (!stmt.executeStep()) + { + LOGWARNING("%s: There is no rank %s, aborting.", __FUNCTION__, a_RankName.c_str()); + return; + } + RankID = stmt.getColumn(0).getInt(); + } + + // Update the player's rank, if already in DB: + { + SQLite::Statement stmt(m_DB, "UPDATE PlayerRank SET RankID = ?, PlayerName = ? WHERE PlayerUUID = ?"); + stmt.bind(1, RankID); + stmt.bind(2, a_PlayerName); + stmt.bind(3, a_PlayerUUID); + if (stmt.exec() > 0) + { + // Successfully updated the player's rank + return; + } + } + + // The player is not yet in the DB, add them: + SQLite::Statement stmt(m_DB, "INSERT INTO PlayerRank (RankID, PlayerUUID, PlayerName) VALUES (?, ?, ?)"); + stmt.bind(1, RankID); + stmt.bind(2, a_PlayerUUID); + stmt.bind(3, a_PlayerName); + if (stmt.exec() > 0) + { + // Successfully added the player + return; + } + + LOGWARNING("%s: Failed to set player UUID %s to rank %s.", + __FUNCTION__, a_PlayerUUID.c_str(), a_RankName.c_str() + ); + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to set player UUID %s to rank %s: %s", + __FUNCTION__, a_PlayerUUID.c_str(), a_RankName.c_str(), ex.what() + ); + } +} + + + + + +void cRankManager::RemovePlayerRank(const AString & a_PlayerUUID) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + SQLite::Statement stmt(m_DB, "DELETE FROM PlayerRank WHERE PlayerUUID = ?"); + stmt.bind(1, a_PlayerUUID); + stmt.exec(); + } + catch(const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to remove rank from player UUID %s: %s", + __FUNCTION__, a_PlayerUUID.c_str(), ex.what() + ); + } +} + + + + + +void cRankManager::SetRankVisuals( + const AString & a_RankName, + const AString & a_MsgPrefix, + const AString & a_MsgSuffix, + const AString & a_MsgNameColorCode +) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + SQLite::Statement stmt(m_DB, "UPDATE Rank SET MsgPrefix = ?, MsgSuffix = ?, MsgNameColorCode = ? WHERE Name = ?"); + stmt.bind(1, a_MsgPrefix); + stmt.bind(2, a_MsgSuffix); + stmt.bind(3, a_MsgNameColorCode); + stmt.bind(4, a_RankName); + if (!stmt.executeStep()) + { + LOGINFO("%s: Rank %s not found, visuals not set.", __FUNCTION__, a_RankName.c_str()); + } + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to get ranks from DB: %s", __FUNCTION__, ex.what()); + } +} + + + + + +bool cRankManager::GetRankVisuals( + const AString & a_RankName, + AString & a_MsgPrefix, + AString & a_MsgSuffix, + AString & a_MsgNameColorCode +) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + SQLite::Statement stmt(m_DB, "SELECT MsgPrefix, MsgSuffix, MsgNameColorCode FROM Rank WHERE Name = ?"); + stmt.bind(1, a_RankName); + if (!stmt.executeStep()) + { + // Rank not found + return false; + } + a_MsgPrefix = stmt.getColumn(0).getText(); + a_MsgSuffix = stmt.getColumn(1).getText(); + a_MsgNameColorCode = stmt.getColumn(2).getText(); + return true; + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to get ranks from DB: %s", __FUNCTION__, ex.what()); + } + return false; +} + + + + + +bool cRankManager::RankExists(const AString & a_RankName) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + SQLite::Statement stmt(m_DB, "SELECT * FROM Rank WHERE Name = ?"); + stmt.bind(1, a_RankName); + if (stmt.executeStep()) + { + // The rank was found + return true; + } + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to query DB for rank %s: %s", __FUNCTION__, a_RankName.c_str(), ex.what()); + } + return false; +} + + + + + +bool cRankManager::GroupExists(const AString & a_GroupName) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + SQLite::Statement stmt(m_DB, "SELECT * FROM PermGroup WHERE Name = ?"); + stmt.bind(1, a_GroupName); + if (stmt.executeStep()) + { + // The group was found + return true; + } + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to query DB for group %s: %s", __FUNCTION__, a_GroupName.c_str(), ex.what()); + } + return false; +} + + + + + +bool cRankManager::IsPlayerRankSet(const AString & a_PlayerUUID) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + SQLite::Statement stmt(m_DB, "SELECT * FROM PlayerRank WHERE PlayerUUID = ?"); + stmt.bind(1, a_PlayerUUID); + if (stmt.executeStep()) + { + // The player UUID was found, they have a rank + return true; + } + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to query DB for player UUID %s: %s", __FUNCTION__, a_PlayerUUID.c_str(), ex.what()); + } + return false; +} + + + + + +bool cRankManager::IsGroupInRank(const AString & a_GroupName, const AString & a_RankName) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + SQLite::Statement stmt(m_DB, + "SELECT * FROM Rank " + "LEFT JOIN RankPermGroup ON Rank.RankID = RankPermGroup.RankID " + "LEFT JOIN PermGroup ON PermGroup.PermGroupID = RankPermGroup.PermGroupID " + "WHERE Rank.Name = ? AND PermGroup.Name = ?" + ); + stmt.bind(1, a_RankName); + stmt.bind(2, a_GroupName); + if (stmt.executeStep()) + { + // The group is in the rank + return true; + } + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to query DB: %s", __FUNCTION__, ex.what()); + } + return false; +} + + + + + +bool cRankManager::IsPermissionInGroup(const AString & a_Permission, const AString & a_GroupName) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + SQLite::Statement stmt(m_DB, + "SELECT * FROM PermissionItem " + "LEFT JOIN PermGroup ON PermGroup.PermGroupID = PermissionItem.PermGroupID " + "WHERE PermissionItem.Permission = ? AND PermGroup.Name = ?" + ); + stmt.bind(1, a_Permission); + stmt.bind(2, a_GroupName); + if (stmt.executeStep()) + { + // The permission is in the group + return true; + } + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to query DB: %s", __FUNCTION__, ex.what()); + } + return false; +} + + + + + +void cRankManager::NotifyNameUUID(const AString & a_PlayerName, const AString & a_UUID) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + SQLite::Statement stmt(m_DB, "UPDATE PlayerRank SET PlayerName = ? WHERE PlayerUUID = ?"); + stmt.bind(1, a_PlayerName); + stmt.bind(2, a_UUID); + stmt.exec(); + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to update DB: %s", __FUNCTION__, ex.what()); + } +} + + + + + +bool cRankManager::SetDefaultRank(const AString & a_RankName) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + // Find the rank's ID: + int RankID = 0; + { + SQLite::Statement stmt(m_DB, "SELECT RankID FROM Rank WHERE Name = ?"); + stmt.bind(1, a_RankName); + if (!stmt.executeStep()) + { + LOGINFO("%s: Cannot set rank %s as the default, it does not exist.", __FUNCTION__, a_RankName.c_str()); + return false; + } + } + + // Set the rank as the default: + { + SQLite::Statement stmt(m_DB, "UPDATE DefaultRank SET RankID = ?"); + stmt.bind(1, RankID); + if (stmt.exec() < 1) + { + // Failed to update, there might be none in the DB, try inserting: + SQLite::Statement stmt2(m_DB, "INSERT INTO DefaultRank (RankID) VALUES (?)"); + stmt2.bind(1, RankID); + if (stmt2.exec() < 1) + { + LOGINFO("%s: Cannot update the default rank in the DB to %s.", __FUNCTION__, a_RankName.c_str()); + return false; + } + } + } + + // Set the internal cache: + m_DefaultRank = a_RankName; + return true; + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to update DB: %s", __FUNCTION__, ex.what()); + return false; + } +} + + + + + +bool cRankManager::AreDBTablesEmpty(void) +{ + return ( + IsDBTableEmpty("Rank") && + IsDBTableEmpty("PlayerRank") && + IsDBTableEmpty("PermGroup") && + IsDBTableEmpty("RankPermGroup") && + IsDBTableEmpty("PermissionItem") && + IsDBTableEmpty("DefaultRank") + ); +} + + + + + +bool cRankManager::IsDBTableEmpty(const AString & a_TableName) +{ + try + { + SQLite::Statement stmt(m_DB, "SELECT COUNT(*) FROM " + a_TableName); + return (stmt.executeStep() && (stmt.getColumn(0).getInt() == 0)); + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to query DB: %s", __FUNCTION__, ex.what()); + } + return false; +} + + + + + +void cRankManager::CreateDefaults(void) +{ + // Wrap everything in a big transaction to speed things up: + cMassChangeLock Lock(*this); + + // Create ranks: + AddRank("Default", "", "", ""); + AddRank("VIP", "", "", ""); + AddRank("Operator", "", "", ""); + AddRank("Admin", "", "", ""); + + // Create groups: + AddGroup("Default"); + AddGroup("Kick"); + AddGroup("Teleport"); + AddGroup("Everything"); + + // Add groups to ranks: + AddGroupToRank("Default", "Default"); + AddGroupToRank("Teleport", "VIP"); + AddGroupToRank("Teleport", "Operator"); + AddGroupToRank("Kick", "Operator"); + AddGroupToRank("Everything", "Admin"); + + // Add permissions to groups: + AddPermissionToGroup("core.build", "Default"); + AddPermissionToGroup("core.tp", "Teleport"); + AddPermissionToGroup("core.kick", "Kick"); + AddPermissionToGroup("*", "Everything"); + + // Set the default rank: + SetDefaultRank("Default"); +} + + + + diff --git a/src/RankManager.h b/src/RankManager.h new file mode 100644 index 000000000..f364bba6a --- /dev/null +++ b/src/RankManager.h @@ -0,0 +1,246 @@ + +// RankManager.h + +// Declares the cRankManager class that represents the rank manager responsible for assigning permissions and message visuals to players + + + + +#pragma once + +#include "SQLiteCpp/Database.h" +#include "SQLiteCpp/Transaction.h" + + + + + +class cMojangAPI; + + + + + +class cRankManager +{ +public: + /** Acquire this lock to perform mass changes. + Improves performance by wrapping everything into a transaction. + Makes sure that no other thread is accessing the DB. */ + class cMassChangeLock + { + public: + cMassChangeLock(cRankManager & a_RankManager) : + m_Lock(a_RankManager.m_CS), + m_Transaction(a_RankManager.m_DB) + { + } + + ~cMassChangeLock() + { + m_Transaction.commit(); + } + + protected: + cCSLock m_Lock; + SQLite::Transaction m_Transaction; + }; + + + /** Creates the rank manager. Needs to be initialized before other use. */ + cRankManager(void); + + ~cRankManager(); + + /** Initializes the rank manager. Performs migration and default-setting if no data is found in the DB. + The a_MojangAPI param is used when migrating from old ini files, to look up player UUIDs. */ + void Initialize(cMojangAPI & a_MojangAPI); + + /** Returns the name of the rank that the specified player has assigned to them. + If the player has no rank assigned, returns an empty string (NOT the default rank). */ + AString GetPlayerRankName(const AString & a_PlayerUUID); + + /** Returns the names of Groups that the specified player has assigned to them. */ + AStringVector GetPlayerGroups(const AString & a_PlayerUUID); + + /** Returns the permissions that the specified player has assigned to them. + If the player has no rank assigned to them, returns the default rank's permissions. */ + AStringVector GetPlayerPermissions(const AString & a_PlayerUUID); + + /** Returns the names of groups that the specified rank has assigned to it. + Returns an empty vector if the rank doesn't exist. */ + AStringVector GetRankGroups(const AString & a_RankName); + + /** Returns the permissions that the specified group has assigned to it. + Returns an empty vector if the group doesn't exist. */ + AStringVector GetGroupPermissions(const AString & a_GroupName); + + /** Returns all permissions that the specified rank has assigned to it, through all its groups. + Returns an empty vector if the rank doesn't exist. Any non-existent groups are ignored. */ + AStringVector GetRankPermissions(const AString & a_RankName); + + /** Returns the names of all defined ranks. */ + AStringVector GetAllRanks(void); + + /** Returns the names of all permission groups. */ + AStringVector GetAllGroups(void); + + /** Returns all the distinct permissions that are stored in the DB. */ + AStringVector GetAllPermissions(void); + + /** Returns the message visuals (prefix, postfix, color) for the specified player. + Returns true if the visuals were read from the DB, false if not (player not found etc). */ + bool GetPlayerMsgVisuals( + const AString & a_PlayerUUID, + AString & a_MsgPrefix, + AString & a_MsgSuffix, + AString & a_MsgNameColorCode + ); + + /** Adds a new rank. No action if the rank already exists. */ + void AddRank( + const AString & a_RankName, + const AString & a_MsgPrefix, + const AString & a_MsgSuffix, + const AString & a_MsgNameColorCode + ); + + /** Adds a new permission group. No action if such a group already exists. */ + void AddGroup(const AString & a_GroupName); + + /** Bulk-adds groups. Group names that already exist are silently skipped. */ + void AddGroups(const AStringVector & a_GroupNames); + + /** Adds the specified permission group to the specified rank. + Fails if the rank or group names are not found. + Returns true if successful, false on error. */ + bool AddGroupToRank(const AString & a_GroupName, const AString & a_RankName); + + /** Adds the specified permission to the specified permission group. + Fails if the permission group name is not found. + Returns true if successful, false on error. */ + bool AddPermissionToGroup(const AString & a_Permission, const AString & a_GroupName); + + /** Adds the specified permissions to the specified permission group. + Fails if the permission group name is not found. + Returns true if successful, false on error. */ + bool AddPermissionsToGroup(const AStringVector & a_Permissions, const AString & a_GroupName); + + /** Removes the specified rank. + All players assigned to that rank will be re-assigned to a_ReplacementRankName. + If a_ReplacementRankName is empty or not a valid rank, the player will be removed from the DB, + which means they will receive the default rank the next time they are queried. + If the rank being removed is the default rank, the default will be changed to the replacement + rank; the operation fails if there's no replacement. */ + void RemoveRank(const AString & a_RankName, const AString & a_ReplacementRankName); + + /** Removes the specified group completely. + The group will first be removed from all ranks using it, and then removed itself. */ + void RemoveGroup(const AString & a_GroupName); + + /** Removes the specified group from the specified rank. + The group will stay defined, even if no rank is using it. */ + void RemoveGroupFromRank(const AString & a_GroupName, const AString & a_RankName); + + /** Removes the specified permission from the specified group. */ + void RemovePermissionFromGroup(const AString & a_Permission, const AString & a_GroupName); + + /** Renames the specified rank. No action if the rank name is not found. + Fails if the new name is already used. + Updates the cached m_DefaultRank if the default rank is being renamed. + Returns true on success, false on failure. */ + bool RenameRank(const AString & a_OldName, const AString & a_NewName); + + /** Renames the specified group. No action if the rank name is not found. + Fails if the new name is already used. + Returns true on success, false on failure. */ + bool RenameGroup(const AString & a_OldName, const AString & a_NewName); + + /** Sets the specified player's rank. + If the player already had rank assigned to them, it is overwritten with the new rank and name. + Note that this doesn't change the cPlayer if the player is already connected, you need to update all the + cPlayer instances manually. + The PlayerName is provided for reference, so that GetRankPlayerNames() can work. */ + void SetPlayerRank(const AString & a_PlayerUUID, const AString & a_PlayerName, const AString & a_RankName); + + /** Removes the player's rank assignment. The player is left without a rank. + Note that this doesn't change the cPlayer instances for the already connected players, you need to update + all the instances manually. + No action if the player has no rank assigned to them already. */ + void RemovePlayerRank(const AString & a_PlayerUUID); + + /** Sets the message visuals of an existing rank. No action if the rank name is not found. */ + void SetRankVisuals( + const AString & a_RankName, + const AString & a_MsgPrefix, + const AString & a_MsgSuffix, + const AString & a_MsgNameColorCode + ); + + /** Returns the message visuals of an existing rank. + Returns true if successful, false on error (rank doesn't exist). */ + bool GetRankVisuals( + const AString & a_RankName, + AString & a_MsgPrefix, + AString & a_MsgSuffix, + AString & a_MsgNameColorCode + ); + + /** Returns true iff the specified rank exists in the DB. */ + bool RankExists(const AString & a_RankName); + + /** Returns true iff the specified group exists in the DB. */ + bool GroupExists(const AString & a_GroupName); + + /** Returns true iff the specified player has a rank assigned to them in the DB. */ + bool IsPlayerRankSet(const AString & a_PlayerUUID); + + /** Returns true iff the specified rank contains the specified group. */ + bool IsGroupInRank(const AString & a_GroupName, const AString & a_RankName); + + /** Returns true iff the specified group contains the specified permission. */ + bool IsPermissionInGroup(const AString & a_Permission, const AString & a_GroupName); + + /** Called by cMojangAPI whenever the playername-uuid pairing is discovered. Updates the DB. */ + void NotifyNameUUID(const AString & a_PlayerName, const AString & a_UUID); + + /** Sets the specified rank as the default rank. + Returns true on success, false on failure (rank not found). */ + bool SetDefaultRank(const AString & a_RankName); + + /** Returns the name of the default rank. */ + const AString & GetDefaultRank(void) const { return m_DefaultRank; } + +protected: + + /** The database storage for all the data. Protected by m_CS. */ + SQLite::Database m_DB; + + /** The name of the default rank. Kept as a cache so that queries for it don't need to go through the DB. */ + AString m_DefaultRank; + + /** The mutex protecting m_DB and m_DefaultRank against multi-threaded access. */ + cCriticalSection m_CS; + + /** Set to true once the manager is initialized. */ + bool m_IsInitialized; + + /** The MojangAPI instance that is used for translating playernames to UUIDs. + Set in Initialize(), may be NULL. */ + cMojangAPI * m_MojangAPI; + + + /** Returns true if all the DB tables are empty, indicating a fresh new install. */ + bool AreDBTablesEmpty(void); + + /** Returns true iff the specified DB table is empty. + If there's an error while querying, returns false. */ + bool IsDBTableEmpty(const AString & a_TableName); + + /** Creates a default set of ranks / groups / permissions. */ + void CreateDefaults(void); +} ; + + + + diff --git a/src/Root.cpp b/src/Root.cpp index ee0d9b835..ef66f9870 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -6,7 +6,6 @@ #include "World.h" #include "WebAdmin.h" #include "FurnaceRecipe.h" -#include "GroupManager.h" #include "CraftingRecipes.h" #include "Bindings/PluginManager.h" #include "MonsterConfig.h" @@ -47,7 +46,6 @@ cRoot::cRoot(void) : m_InputThread(NULL), m_Server(NULL), m_MonsterConfig(NULL), - m_GroupManager(NULL), m_CraftingRecipes(NULL), m_FurnaceRecipe(NULL), m_WebAdmin(NULL), @@ -161,7 +159,7 @@ void cRoot::Start(void) m_WebAdmin->Init(); LOGD("Loading settings..."); - m_GroupManager = new cGroupManager(); + m_RankManager.Initialize(m_MojangAPI); m_CraftingRecipes = new cCraftingRecipes; m_FurnaceRecipe = new cFurnaceRecipe(); @@ -240,8 +238,6 @@ void cRoot::Start(void) LOGD("Unloading recipes..."); delete m_FurnaceRecipe; m_FurnaceRecipe = NULL; delete m_CraftingRecipes; m_CraftingRecipes = NULL; - LOGD("Forgetting groups..."); - delete m_GroupManager; m_GroupManager = NULL; LOGD("Unloading worlds..."); UnloadWorlds(); @@ -555,17 +551,6 @@ void cRoot::SaveAllChunks(void) -void cRoot::ReloadGroups(void) -{ - LOG("Reload groups ..."); - m_GroupManager->LoadGroups(); - m_GroupManager->CheckUsers(); -} - - - - - void cRoot::BroadcastChat(const AString & a_Message, eMessageType a_ChatPrefix) { for (WorldMap::iterator itr = m_WorldsByName.begin(), end = m_WorldsByName.end(); itr != end; ++itr) diff --git a/src/Root.h b/src/Root.h index 6840efcbe..9bc975889 100644 --- a/src/Root.h +++ b/src/Root.h @@ -5,6 +5,7 @@ #include "Protocol/MojangAPI.h" #include "HTTPServer/HTTPServer.h" #include "Defines.h" +#include "RankManager.h" @@ -13,7 +14,6 @@ // fwd: class cThread; class cMonsterConfig; -class cGroupManager; class cCraftingRecipes; class cFurnaceRecipe; class cWebAdmin; @@ -78,7 +78,6 @@ public: cMonsterConfig * GetMonsterConfig(void) { return m_MonsterConfig; } - cGroupManager * GetGroupManager (void) { return m_GroupManager; } // tolua_export cCraftingRecipes * GetCraftingRecipes(void) { return m_CraftingRecipes; } // tolua_export cFurnaceRecipe * GetFurnaceRecipe (void) { return m_FurnaceRecipe; } // Exported in ManualBindings.cpp with quite a different signature @@ -89,6 +88,7 @@ public: cPluginManager * GetPluginManager (void) { return m_PluginManager; } // tolua_export cAuthenticator & GetAuthenticator (void) { return m_Authenticator; } cMojangAPI & GetMojangAPI (void) { return m_MojangAPI; } + cRankManager & GetRankManager (void) { return m_RankManager; } /** Queues a console command for execution through the cServer class. The command will be executed in the tick thread @@ -122,9 +122,6 @@ public: /// Saves all chunks in all worlds void SaveAllChunks(void); // tolua_export - /// Reloads all the groups - void ReloadGroups(void); // tolua_export - /// Calls the callback for each player in all worlds bool ForEachPlayer(cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << @@ -187,13 +184,13 @@ private: cServer * m_Server; cMonsterConfig * m_MonsterConfig; - cGroupManager * m_GroupManager; cCraftingRecipes * m_CraftingRecipes; cFurnaceRecipe * m_FurnaceRecipe; cWebAdmin * m_WebAdmin; cPluginManager * m_PluginManager; cAuthenticator m_Authenticator; cMojangAPI m_MojangAPI; + cRankManager m_RankManager; cHTTPServer m_HTTPServer; bool m_bStop; diff --git a/src/Server.cpp b/src/Server.cpp index cbb9fba4d..958fe83c8 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -11,7 +11,6 @@ #include "World.h" #include "ChunkDef.h" #include "Bindings/PluginManager.h" -#include "GroupManager.h" #include "ChatColor.h" #include "Entities/Player.h" #include "Inventory.h" @@ -471,25 +470,17 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac PrintHelp(split, a_Output); return; } - if (split[0] == "reload") + else if (split[0] == "reload") { cPluginManager::Get()->ReloadPlugins(); - cRoot::Get()->ReloadGroups(); return; } - if (split[0] == "reloadplugins") + else if (split[0] == "reloadplugins") { cPluginManager::Get()->ReloadPlugins(); return; } - if (split[0] == "reloadgroups") - { - cRoot::Get()->ReloadGroups(); - a_Output.Out("Groups reloaded!"); - a_Output.Finished(); - return; - } - if (split[0] == "load") + else if (split[0] == "load") { if (split.size() > 1) { @@ -504,8 +495,7 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac return; } } - - if (split[0] == "unload") + else if (split[0] == "unload") { if (split.size() > 1) { @@ -521,21 +511,21 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac } // There is currently no way a plugin can do these (and probably won't ever be): - if (split[0].compare("chunkstats") == 0) + else if (split[0].compare("chunkstats") == 0) { cRoot::Get()->LogChunkStats(a_Output); a_Output.Finished(); return; } #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) - if (split[0].compare("dumpmem") == 0) + else if (split[0].compare("dumpmem") == 0) { LeakFinderXmlOutput Output("memdump.xml"); DumpUsedMemory(&Output); return; } - if (split[0].compare("killmem") == 0) + else if (split[0].compare("killmem") == 0) { for (;;) { @@ -544,7 +534,7 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac } #endif - if (cPluginManager::Get()->ExecuteConsoleCommand(split, a_Output)) + else if (cPluginManager::Get()->ExecuteConsoleCommand(split, a_Output)) { a_Output.Finished(); return; diff --git a/src/Server.h b/src/Server.h index c1640b388..f20e6932f 100644 --- a/src/Server.h +++ b/src/Server.h @@ -120,7 +120,7 @@ public: // tolua_export const AString & GetPublicKeyDER(void) const { return m_PublicKeyDER; } /** Returns true if authentication has been turned on in server settings. */ - bool ShouldAuthenticate(void) const { return m_ShouldAuthenticate; } + bool ShouldAuthenticate(void) const { return m_ShouldAuthenticate; } // tolua_export /** Returns true if offline UUIDs should be used to load data for players whose normal UUIDs cannot be found. Loaded from the settings.ini [PlayerData].LoadOfflinePlayerData setting. */ |