diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index 64ba80c5f..fe56ecee8 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -942,24 +942,6 @@ cFile:Delete("/usr/bin/virus.exe"); Inherits = "cEntity", }, - cGroup = - { - Desc = [[ - This class represents a group {{cPlayer|players}} can be in. Groups define the permissions players - have, and optionally the color of their name in the chat. - ]], - Functions = - { - SetName = { Return = "" }, - GetName = { Return = "string" }, - SetColor = { Return = "" }, - GetColor = { Return = "string" }, - AddCommand = { Return = "" }, - AddPermission = { Return = "" }, - InheritFrom = { Return = "" }, - }, - }, -- cGroup - cIniFile = { Desc = [[ @@ -1760,7 +1742,6 @@ a_Player:OpenWindow(Window); Functions = { AddFoodExhaustion = { Params = "Exhaustion", Return = "", Notes = "Adds the specified number to the food exhaustion. Only positive numbers expected." }, - AddToGroup = { Params = "GroupName", Return = "", Notes = "Temporarily adds the player to the specified group. The assignment is lost when the player disconnects." }, CalcLevelFromXp = { Params = "XPAmount", Return = "number", Notes = "(STATIC) Returns the level which is reached with the specified amount of XP. Inverse of XpForLevel()." }, CanFly = { Return = "bool", Notes = "Returns if the player is able to fly." }, CloseWindow = { Params = "[CanRefuse]", Return = "", Notes = "Closes the currently open UI window. If CanRefuse is true (default), the window may refuse the closing." }, @@ -1770,7 +1751,7 @@ a_Player:OpenWindow(Window); FoodPoison = { Params = "NumTicks", Return = "", Notes = "Starts the food poisoning for the specified amount of ticks; if already foodpoisoned, sets FoodPoisonedTicksRemaining to the larger of the two" }, ForceSetSpeed = { Params = "{{Vector3d|Direction}}", Notes = "Forces the player to move to the given direction." }, GetClientHandle = { Params = "", Return = "{{cClientHandle}}", Notes = "Returns the client handle representing the player's connection. May be nil (AI players)." }, - GetColor = { Return = "string", Notes = "Returns the full color code to be used for this player (based on the first group). Prefix player messages with this code." }, + GetColor = { Return = "string", Notes = "Returns the full color code to be used for this player's messages (based on their rank). Prefix player messages with this code." }, GetCurrentXp = { Params = "", Return = "number", Notes = "Returns the current amount of XP" }, GetEffectiveGameMode = { Params = "", Return = "{{Globals#GameMode|GameMode}}", Notes = "(OBSOLETE) Returns the current resolved game mode of the player. If the player is set to inherit the world's gamemode, returns that instead. See also GetGameMode() and IsGameModeXXX() functions. Note that this function is the same as GetGameMode(), use that function instead." }, GetEquippedItem = { Params = "", Return = "{{cItem}}", Notes = "Returns the item that the player is currently holding; empty item if holding nothing." }, @@ -1784,7 +1765,6 @@ a_Player:OpenWindow(Window); GetFoodSaturationLevel = { Params = "", Return = "number", Notes = "Returns the food saturation (overcharge of the food level, is depleted before food level)" }, GetFoodTickTimer = { Params = "", Return = "", Notes = "Returns the number of ticks past the last food-based heal or damage action; when this timer reaches 80, a new heal / damage is applied." }, GetGameMode = { Return = "{{Globals#GameMode|GameMode}}", Notes = "Returns the player's gamemode. The player may have their gamemode unassigned, in which case they inherit the gamemode from the current {{cWorld|world}}.
NOTE: Instead of comparing the value returned by this function to the gmXXX constants, use the IsGameModeXXX() functions. These functions handle the gamemode inheritance automatically."}, - GetGroups = { Return = "array-table of {{cGroup}}", Notes = "Returns all the groups that this player is member of, as a table. The groups are stored in the array part of the table, beginning with index 1."}, GetIP = { Return = "string", Notes = "Returns the IP address of the player, if available. Returns an empty string if there's no IP to report."}, GetInventory = { Return = "{{cInventory|Inventory}}", Notes = "Returns the player's inventory"}, GetMaxSpeed = { Params = "", Return = "number", Notes = "Returns the player's current maximum speed, relative to the game default speed. Takes into account the sprinting / flying status." }, @@ -1807,15 +1787,13 @@ a_Player:OpenWindow(Window); IsGameModeAdventure = { Params = "", Return = "bool", Notes = "Returns true if the player is in the gmAdventure gamemode, or has their gamemode unset and the world is a gmAdventure world." }, IsGameModeCreative = { Params = "", Return = "bool", Notes = "Returns true if the player is in the gmCreative gamemode, or has their gamemode unset and the world is a gmCreative world." }, IsGameModeSurvival = { Params = "", Return = "bool", Notes = "Returns true if the player is in the gmSurvival gamemode, or has their gamemode unset and the world is a gmSurvival world." }, - IsInGroup = { Params = "GroupNameString", Return = "bool", Notes = "Returns true if the player is a member of the specified group." }, IsOnGround = { Params = "", Return = "bool", Notes = "Returns true if the player is on ground (not falling, not jumping, not flying)" }, IsSatiated = { Params = "", Return = "bool", Notes = "Returns true if the player is satiated (cannot eat)." }, IsVisible = { Params = "", Return = "bool", Notes = "Returns true if the player is visible to other players" }, - LoadPermissionsFromDisk = { Params = "", Return = "", Notes = "Reloads the player's permissions from the disk. This loses any temporary changes made to the player's groups." }, + LoadRank = { Params = "", Return = "", Notes = "Reloads the player's rank, message visuals and permissions from the {{cRankManager}}, based on the player's current rank." }, MoveTo = { Params = "{{Vector3d|NewPosition}}", Return = "Tries to move the player into the specified position." }, MoveToWorld = { Params = "WorldName", Return = "bool", Return = "Moves the player to the specified world. Returns true if successful." }, OpenWindow = { Params = "{{cWindow|Window}}", Return = "", Notes = "Opens the specified UI window for the player." }, - RemoveFromGroup = { Params = "GroupName", Return = "", Notes = "Temporarily removes the player from the specified group. This change is lost when the player disconnects." }, Respawn = { Params = "", Return = "", Notes = "Restores the health, extinguishes fire, makes visible and sends the Respawn packet." }, SendMessage = { Params = "Message", Return = "", Notes = "Sends the specified message to the player." }, SendMessageFailure = { Params = "Message", Return = "", Notes = "Prepends Rose [INFO] / colours entire text (depending on ShouldUseChatPrefixes()) and sends message to player. For a command that failed to run because of insufficient permissions, etc." }, @@ -2004,6 +1982,71 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage); }, }, -- cPluginManager + cRankManager = + { + Desc = [[ + Manages the players' permissions. The players are assigned a single rank, which contains groups of + permissions. The functions in this class query or modify these.

+

+ All the functions are static, call them using the cRankManager:Function() convention.

+

+ The players are identified by their UUID, to support player renaming.

+

+ The rank also contains specific "mesage visuals" - bits that are used for formatting messages from the + players. There's a message prefix, which is put in front of every message the player sends, and the + message suffix that is appended to each message. There's also a PlayerNameColorCode, which holds the + color that is used for the player's name in the messages.

+

+ Each rank can contain any number of permission groups. These groups allow for an easier setup of the + permissions - you can share groups among ranks, so the usual approach is to group similar permissions + together and add that group to any rank that should use those permissions.

+

+ Permissions are added to individual groups. Each group can support unlimited permissions. Note that + adding a permission to a group will make the permission available to all the ranks that contain that + permission group.

+

+ One rank is reserved as the Default rank. All players that don't have an explicit rank assigned to them + will behave as if assigned to this rank. The default rank can be changed to any other rank at any time. + Note that the default rank cannot be removed from the RankManager - RemoveRank() will change the default + rank to the replacement rank, if specified, and fail if no replacement rank is specified. Renaming the + default rank using RenameRank() will change the default rank to the new name. + ]], + Functions = + { + AddGroup = { Params = "GroupName", Return = "", Notes = "Adds the group of the specified name. Logs a warning and does nothing if the group already exists." }, + AddGroupToRank = { Params = "GroupName, RankName", Return = "bool", Notes = "Adds the specified group to the specified rank. Returns true on success, false on failure - if the group name or the rank name is not found." }, + AddPermissionToGroup = { Params = "Permission, GroupName", Return = "bool", Notes = "Adds the specified permission to the specified group. Returns true on success, false on failure - if the group name is not found." }, + AddRank = { Params = "RankName, MsgPrefix, MsgSuffix, MsgNameColorCode", Return = "", Notes = "Adds a new rank of the specified name and with the specified message visuals. Logs an info message and does nothing if the rank already exists." }, + GetAllGroups = { Params = "", Return = "array-table of groups' names", Notes = "Returns an array-table containing the names of all the groups that are known to the manager." }, + GetAllPermissions = { Params = "", Return = "array-table of permissions", Notes = "Returns an array-table containing all the permissions that are known to the manager." }, + GetAllRanks = { Params = "", Return = "array-table of ranks' names", Notes = "Returns an array-table containing the names of all the ranks that are known to the manager." }, + GetDefaultRank = { Params = "", Return = "string", Notes = "Returns the name of the default rank. " }, + GetGroupPermissions = { Params = "GroupName", Return = "array-table of permissions", Notes = "Returns an array-table containing the permissions that the specified group contains." }, + GetPlayerGroups = { Params = "PlayerUUID", Return = "array-table of groups' names", Notes = "Returns an array-table of the names of the groups that are assigned to the specified player through their rank. Returns an empty table if the player is not known or has no rank or groups assigned to them." }, + GetPlayerMsgVisuals = { Params = "PlayerUUID", Return = "MsgPrefix, MsgSuffix, MsgNameColorCode", Notes = "Returns the message visuals assigned to the player. If the player is not explicitly assigned a rank, the default rank's visuals are returned. If there is an error, no value is returned at all." }, + GetPlayerPermissions = { Params = "PlayerUUID", Return = "array-table of permissions", Notes = "Returns the permissions that the specified player is assigned through their rank. Returns the default rank's permissions if the player has no explicit rank assigned to them. Returns an empty array on error." }, + GetPlayerRankName = { Params = "PlayerUUID", Return = "RankName", Notes = "Returns the name of the rank that is assigned to the specified player. An empty string (NOT the default rank) is returned if the player has no rank assigned to them." }, + GetRankGroups = { Params = "RankName", Return = "array-table of groups' names", Notes = "Returns an array-table of the names of all the groups that are assigned to the specified rank. Returns an empty table if there is no such rank." }, + GetRankPermissions = { Params = "RankName", Return = "array-table of permissions", Notes = "Returns an array-table of all the permissions that are assigned to the specified rank through its groups. Returns an empty table if there is no such rank." }, + GetRankVisuals = { Params = "RankName", Return = "MsgPrefix, MsgSuffix, MsgNameColorCode", Notes = "Returns the message visuals for the specified rank. Returns no value if the specified rank does not exist." }, + GroupExists = { Params = "GroupName", Return = "bool", Notes = "Returns true iff the specified group exists." }, + IsGroupInRank = { Params = "GroupName, RankName", Return = "bool", Notes = "Returns true iff the specified group is assigned to the specified rank." }, + IsPermissionInGroup = { Params = "Permission, GroupName", Return = "bool", Notes = "Returns true iff the specified permission is assigned to the specified group." }, + IsPlayerRankSet = { Params = "PlayerUUID", Return = "bool", Notes = "Returns true iff the specified player has a rank assigned to them." }, + RankExists = { Params = "RankName", Return = "bool", Notes = "Returns true iff the specified rank exists." }, + RemoveGroup = { Params = "GroupName", Return = "", Notes = "Removes the specified group completely. The group will be removed from all the ranks using it and then erased from the manager. Logs an info message and does nothing if the group doesn't exist." }, + RemoveGroupFromRank = { Params = "GroupName, RankName", Return = "", Notes = "Removes the specified group from the specified rank. The group will still exist, even if it isn't assigned to any rank. Logs an info message and does nothing if the group or rank doesn't exist." }, + RemovePermissionFromGroup = { Params = "Permission, GroupName", Return = "", Notes = "Removes the specified permission from the specified group. Logs an info message and does nothing if the group doesn't exist." }, + RemovePlayerRank = { Params = "PlayerUUID", Return = "", Notes = "Removes the player's rank; the player's 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." }, + RemoveRank = { Params = "RankName, [ReplacementRankName]", Return = "", Notes = "Removes the specified rank. If ReplacementRankName is given, the players that have RankName will get their rank set to ReplacementRankName. If it isn't given, or is an invalid rank, the players will be removed from the manager, their ranks will be unset completely. Logs an info message and does nothing if the rank is not found." }, + RenameGroup = { Params = "OldName, NewName", Return = "", Notes = "Renames the specified group. Logs an info message and does nothing if the group is not found or the new name is already used." }, + RenameRank = { Params = "OldName, NewName", Return = "", Notes = "Renames the specified rank. Logs an info message and does nothing if the rank is not found or the new name is already used." }, + SetDefaultRank = { Params = "RankName", Return = "bool", Notes = "Sets the specified rank as the default rank. Returns true on success, false on failure (rank doesn't exist)." }, + SetPlayerRank = { Params = "PlayerUUID, PlayerName, RankName", Return = "", Notes = "Updates the rank for the specified player. The player name is provided for reference, the UUID is used for identification. Logs a warning and does nothing if the rank is not found." }, + SetRankVisuals = { Params = "RankName, MsgPrefix, MsgSuffix, MsgNameColorCode", Return = "", Notes = "Updates the rank's message visuals. Logs an info message and does nothing if rank not found." }, + }, + }, -- cRankManager + cRoot = { Desc = [[ @@ -2032,7 +2075,6 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage); GetDefaultWorld = { Params = "", Return = "{{cWorld|cWorld}}", Notes = "Returns the world object from the default world." }, GetFurnaceFuelBurnTime = { Params = "{{cItem|Fuel}}", Return = "number", Notes = "(STATIC) Returns the number of ticks for how long the item would fuel a furnace. Returns zero if not a fuel." }, GetFurnaceRecipe = { Params = "{{cItem|InItem}}", Return = "{{cItem|OutItem}}, NumTicks, {{cItem|InItem}}", Notes = "(STATIC) Returns the furnace recipe for smelting the specified input. If a recipe is found, returns the smelted result, the number of ticks required for the smelting operation, and the input consumed (note that MCServer supports smelting M items into N items and different smelting rates). If no recipe is found, returns no value." }, - GetGroupManager = { Params = "", Return = "{{cGroupManager|cGroupManager}}", Notes = "Returns the cGroupManager object." }, GetPhysicalRAMUsage = { Params = "", Return = "number", Notes = "Returns the amount of physical RAM that the entire MCServer process is using, in KiB. Negative if the OS doesn't support this query." }, GetPluginManager = { Params = "", Return = "{{cPluginManager|cPluginManager}}", Notes = "Returns the cPluginManager object." }, GetPrimaryServerVersion = { Params = "", Return = "number", Notes = "Returns the servers primary server version." }, @@ -2116,10 +2158,11 @@ end { GetDescription = { Return = "string", Notes = "Returns the server description set in the settings.ini." }, GetMaxPlayers = { Return = "number", Notes = "Returns the max amount of players who can join the server." }, - SetMaxPlayers = { Params = "number", Notes = "Sets the max amount of players who can join." }, GetNumPlayers = { Return = "number", Notes = "Returns the amount of players online." }, GetServerID = { Return = "string", Notes = "Returns the ID of the server?" }, IsHardcore = { Params = "", Return = "bool", Notes = "Returns true if the server is hardcore (players get banned on death)." }, + SetMaxPlayers = { Params = "number", Notes = "Sets the max amount of players who can join." }, + ShouldAuthenticate = { Params = "", Return = "bool", Notes = "Returns true iff the server is set to authenticate players (\"online mode\")." }, }, }, -- cServer diff --git a/MCServer/Plugins/Core b/MCServer/Plugins/Core index 1b16c23c2..7cc99285a 160000 --- a/MCServer/Plugins/Core +++ b/MCServer/Plugins/Core @@ -1 +1 @@ -Subproject commit 1b16c23c216d359e9fe0334c63deeecc347e69bd +Subproject commit 7cc99285ae5117418f657c3b295ca71f2b75b4f4 diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua index 7e220952e..81cf02f3c 100644 --- a/MCServer/Plugins/Debuggers/Debuggers.lua +++ b/MCServer/Plugins/Debuggers/Debuggers.lua @@ -80,7 +80,8 @@ function Initialize(Plugin) TestBlockAreasString() TestStringBase64() - TestUUIDFromName() + -- TestUUIDFromName() + -- TestRankMgr() --[[ -- Test cCompositeChat usage in console-logging: @@ -352,6 +353,18 @@ end +function TestRankMgr() + LOG("Testing the rank manager") + cRankManager:AddRank("LuaRank") + cRankManager:AddGroup("LuaTestGroup") + cRankManager:AddGroupToRank("LuaTestGroup", "LuaRank") + cRankManager:AddPermissionToGroup("luaperm", "LuaTestGroup") +end + + + + + function TestSQLiteBindings() LOG("Testing SQLite bindings..."); 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) { + // Function signature: cPlayer:GetPermissions() -> {permissions-array} + + // Check the params: + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cPlayer") || + !L.CheckParamEnd (2) + ) + { + return 0; + } + + // Get the params: cPlayer * self = (cPlayer *)tolua_tousertype(tolua_S, 1, NULL); - - const cPlayer::GroupList & AllGroups = self->GetGroups(); - - 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()) + if (self == NULL) { - const cGroup * Group = *iter; - tolua_pushusertype(tolua_S, (void *)Group, "const cGroup"); - lua_rawseti(tolua_S, newTable, index); - ++iter; - ++index; - } - 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()) - { - 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,47 +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; - } - } - Possibilities.erase( itr); - } - - // Nothing that matched :( - return false; -} - - - - - -bool cPlayer::IsInGroup( const AString & a_Group) -{ - for (GroupList::iterator itr = m_ResolvedGroups.begin(); itr != m_ResolvedGroups.end(); ++itr) - { - if (a_Group.compare( (*itr)->GetName().c_str()) == 0) return true; - } + } + } // for itr - m_SplitPermissions[] + + // No granted permission matches return false; } @@ -1465,68 +1391,35 @@ bool cPlayer::IsInGroup( const AString & a_Group) -void cPlayer::ResolvePermissions() +bool cPlayer::PermissionMatches(const AStringVector & a_Permission, const AStringVector & a_Template) { - 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) + // 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++) { - 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_Template[i] == "*") { - m_ResolvedPermissions[ itr->first ] = itr->second; + // Has matched so far and now there's a wildcard in the template, so the permission matches: + return true; + } + if (a_Permission[i] != a_Template[i]) + { + // 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 > 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 + + + + +// 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 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 sUserMap; + + typedef std::map 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 "" if there's only one group, + or ",,...", 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 "" 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") - { - cPluginManager::Get()->ReloadPlugins(); - cRoot::Get()->ReloadGroups(); - return; - } - if (split[0] == "reloadplugins") + else if (split[0] == "reload") { cPluginManager::Get()->ReloadPlugins(); return; } - if (split[0] == "reloadgroups") + else if (split[0] == "reloadplugins") { - cRoot::Get()->ReloadGroups(); - a_Output.Out("Groups reloaded!"); - a_Output.Finished(); + cPluginManager::Get()->ReloadPlugins(); 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. */