summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormadmaxoft <github@xoft.cz>2014-08-13 09:53:33 +0200
committermadmaxoft <github@xoft.cz>2014-08-13 09:53:33 +0200
commite110f7226895b1629f72a52c7953d8f00a1f63c6 (patch)
tree820d8f50c261d69e7a808bfff4386b37bbfd342a
parentMerge remote-tracking branch 'origin/master' into Ranks (diff)
downloadcuberite-e110f7226895b1629f72a52c7953d8f00a1f63c6.tar
cuberite-e110f7226895b1629f72a52c7953d8f00a1f63c6.tar.gz
cuberite-e110f7226895b1629f72a52c7953d8f00a1f63c6.tar.bz2
cuberite-e110f7226895b1629f72a52c7953d8f00a1f63c6.tar.lz
cuberite-e110f7226895b1629f72a52c7953d8f00a1f63c6.tar.xz
cuberite-e110f7226895b1629f72a52c7953d8f00a1f63c6.tar.zst
cuberite-e110f7226895b1629f72a52c7953d8f00a1f63c6.zip
-rw-r--r--src/RankManager.cpp540
-rw-r--r--src/RankManager.h28
-rw-r--r--src/Root.cpp1
3 files changed, 568 insertions, 1 deletions
diff --git a/src/RankManager.cpp b/src/RankManager.cpp
index 3627afadb..9809f1d6c 100644
--- a/src/RankManager.cpp
+++ b/src/RankManager.cpp
@@ -6,6 +6,8 @@
#include "Globals.h"
#include "RankManager.h"
#include "SQLiteCpp/Transaction.h"
+#include "inifile/iniFile.h"
+#include "Protocol/MojangAPI.h"
@@ -219,9 +221,367 @@ protected:
+////////////////////////////////////////////////////////////////////////////////
+/** 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)
+ {
+ 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();
+
+ return true;
+ }
+
+protected:
+
+ /** Container for a group read from an INI file. */
+ struct sGroup
+ {
+ AString m_Name;
+ AString m_Color;
+ AStringVector m_Inherits;
+ AStringVector m_Permissions;
+
+ sGroup(void) {}
+
+ sGroup(const AString & a_Name, const AString & a_Color, const AStringVector & a_Inherits, const AStringVector & a_Permissions):
+ m_Name(a_Name),
+ m_Color(a_Color),
+ m_Inherits(a_Inherits),
+ m_Permissions(a_Permissions)
+ {
+ }
+ };
+ typedef std::map<AString, sGroup> sGroupMap;
+
+
+ /** Container for a single user read from an INI file. */
+ struct sUser
+ {
+ AString m_Name;
+ AStringVector m_Groups;
+
+ /** Assigned by ResolveUserUUIDs() */
+ AString m_UUID;
+
+
+ sUser(void) {}
+
+ sUser(const AString & a_Name, const AStringVector & a_Groups):
+ m_Name(a_Name),
+ m_Groups(a_Groups)
+ {
+ }
+ };
+ typedef std::map<AString, sUser> sUserMap;
+
+ typedef std::map<AString, AString> cStringMap;
+
+
+ /** The parent Rank manager where we will create the groups, ranks and players */
+ cRankManager & m_RankManager;
+
+ /** The player name to UUID resolver */
+ cMojangAPI & m_MojangAPI;
+
+ /** List of all groups read from the ini file */
+ sGroupMap m_Groups;
+
+ /** List of all players read from the ini file. */
+ sUserMap m_Users;
+
+ /** Maps lists of groups to rank names.
+ Each group list is either a simple "<Group>" if there's only one group,
+ or "<PrimaryGroup>,<FirstSecondaryGroup>,<SecondSecondaryGroup>...", where the secondary groups are
+ lowercased and alpha-sorted. This makes the group lists comparable for equivalence, simply by comparing
+ their string names.
+ The ranks are named "<Group>" for single-group players, and "AutoMigratedRank_N" for the composite ranks,
+ where N is a unique number. */
+ cStringMap m_GroupsToRanks;
+
+
+
+ /** Reads the groups from the "groups.ini" file into m_Groups */
+ bool ReadGroups(void)
+ {
+ // Read the file:
+ cIniFile Groups;
+ if (!Groups.ReadFile("groups.ini"))
+ {
+ return false;
+ }
+
+ // Read all the groups into a map:
+ int NumGroups = Groups.GetNumKeys();
+ for (int i = 0; i < NumGroups; i++)
+ {
+ AString GroupName = Groups.GetKeyName(i);
+ AString lcGroupName = StrToLower(GroupName);
+ if (m_Groups.find(lcGroupName) != m_Groups.end())
+ {
+ LOGINFO("groups.ini contains a duplicate definition of group %s, ignoring the latter.", GroupName.c_str());
+ continue;
+ }
+ m_Groups[lcGroupName] = sGroup(
+ GroupName,
+ Groups.GetValue(GroupName, "Color", ""),
+ StringSplitAndTrim(Groups.GetValue(GroupName, "Inherits"), ","),
+ StringSplitAndTrim(Groups.GetValue(GroupName, "Permissions"), ",")
+ );
+ } // for i - Groups' keys
+ return true;
+ }
+
+
+
+ /** Removes non-existent groups from all the groups' inheritance */
+ void CleanGroupInheritance(void)
+ {
+ for (sGroupMap::iterator itrG = m_Groups.begin(), endG = m_Groups.end(); itrG != endG; ++itrG)
+ {
+ AStringVector & Inherits = itrG->second.m_Inherits;
+ for (AStringVector::iterator itrI = Inherits.begin(); itrI != Inherits.end();)
+ {
+ AString lcInherits = StrToLower(*itrI);
+ if (m_Groups.find(lcInherits) != m_Groups.end())
+ {
+ // Inherited group exists, continue checking the next one
+ ++itrI;
+ continue;
+ }
+ // Inherited group doesn't exist, remove it from the list:
+ LOGWARNING("RankMigrator: Group \"%s\" inherits from a non-existent group \"%s\", this inheritance will be ignored.",
+ itrG->second.m_Name.c_str(), itrI->c_str()
+ );
+ AStringVector::iterator itrI2 = itrI;
+ ++itrI2;
+ Inherits.erase(itrI);
+ itrI = itrI2;
+ } // for itrI - Inherits[]
+ } // for itrG - m_Groups[]
+ }
+
+
+
+ /** Reads the users from the "users.ini" file into m_Users */
+ bool ReadUsers(void)
+ {
+ // Read the file:
+ cIniFile Users;
+ if (!Users.ReadFile("users.ini"))
+ {
+ return false;
+ }
+
+ // Read all the users into a map:
+ int NumUsers = Users.GetNumKeys();
+ for (int i = 0; i < NumUsers; i++)
+ {
+ AString UserName = Users.GetKeyName(i);
+ AString lcUserName = StrToLower(UserName);
+ if (m_Users.find(lcUserName) != m_Users.end())
+ {
+ LOGINFO("users.ini contains a duplicate definition of user %s, ignoring the latter.", UserName.c_str());
+ continue;
+ }
+ m_Users[lcUserName] = sUser(
+ UserName,
+ StringSplitAndTrim(Users.GetValue(UserName, "Groups", ""), ",")
+ );
+ } // for i - Users' keys
+ return true;
+ }
+
+
+
+ /** Removes non-existent groups from each user's definition. */
+ void CleanUserGroups(void)
+ {
+ for (sUserMap::iterator itrU = m_Users.begin(), endU = m_Users.end(); itrU != endU; ++itrU)
+ {
+ AStringVector & Groups = itrU->second.m_Groups;
+ for (AStringVector::iterator itrG = Groups.begin(); itrG != Groups.end();)
+ {
+ AString lcGroup = StrToLower(*itrG);
+ if (m_Groups.find(lcGroup) != m_Groups.end())
+ {
+ // Assigned group exists, continue checking the next one
+ ++itrG;
+ continue;
+ }
+ // Assigned group doesn't exist, remove it from the list:
+ LOGWARNING("RankMigrator: User \"%s\" is assigned a non-existent group \"%s\", this assignment will be ignored.",
+ itrU->second.m_Name.c_str(), itrG->c_str()
+ );
+ AStringVector::iterator itrG2 = itrG;
+ ++itrG2;
+ Groups.erase(itrG);
+ itrG = itrG2;
+ } // for itrG - Groups[]
+ } // for itrU - m_Users[]
+ }
+
+
+
+ /** Creates groups based on m_Groups.
+ Ignores group inheritance. */
+ void CreateGroups(void)
+ {
+ // Create each group, with its permissions:
+ for (sGroupMap::const_iterator itr = m_Groups.begin(), end = m_Groups.end(); itr != end; ++itr)
+ {
+ m_RankManager.AddGroup(itr->second.m_Name);
+ m_RankManager.AddPermissionsToGroup(itr->second.m_Permissions, itr->second.m_Name);
+ } // for itr - m_Groups[]
+ }
+
+
+ /** Resolves the UUID of each user in m_Users.
+ If a user doesn't resolve, they are removed and logged in the console. */
+ void ResolveUserUUIDs(void)
+ {
+ // Resolve all PlayerNames at once (the API doesn't like single-name queries):
+ AStringVector PlayerNames;
+ for (sUserMap::const_iterator itr = m_Users.begin(), end = m_Users.end(); itr != end; ++itr)
+ {
+ PlayerNames.push_back(itr->second.m_Name);
+ }
+ m_MojangAPI.GetUUIDsFromPlayerNames(PlayerNames);
+
+ // Assign the UUIDs back to players, remove those not resolved:
+ for (sUserMap::iterator itr = m_Users.begin(); itr != m_Users.end(); )
+ {
+ AString UUID = m_MojangAPI.GetUUIDFromPlayerName(itr->second.m_Name);
+ if (UUID.empty())
+ {
+ LOGWARNING("RankMigrator: Cannot resolve player %s to UUID, player will be left unranked", itr->second.m_Name.c_str());
+ sUserMap::iterator itr2 = itr;
+ ++itr2;
+ m_Users.erase(itr);
+ itr = itr2;
+ }
+ else
+ {
+ itr->second.m_UUID = UUID;
+ ++itr;
+ }
+ }
+ }
+
+
+
+ /** 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:
+ m_RankManager.SetPlayerRank(itr->second.m_UUID, itr->second.m_Name, RankName);
+ } // for itr - m_Users[]
+ }
+};
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cRankManager:
+
cRankManager::cRankManager(void) :
- m_DB("Ranks.sqlite", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)
+ m_DB("Ranks.sqlite", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE),
+ m_IsInitialized(false)
+{
+}
+
+
+
+
+
+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)");
@@ -229,6 +589,22 @@ cRankManager::cRankManager(void) :
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_IsInitialized = true;
+
+ // 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.");
+ return;
+ }
+ }
+
+ // Migration failed.
// TODO: Check if tables empty, add some defaults then
}
@@ -238,6 +614,8 @@ cRankManager::cRankManager(void) :
AString cRankManager::GetPlayerRankName(const AString & a_PlayerUUID)
{
+ ASSERT(m_IsInitialized);
+
try
{
SQLite::Statement stmt(m_DB, "SELECT Rank.Name FROM Rank LEFT JOIN PlayerRank ON Rank.RankID = PlayerRank.RankID WHERE PlayerRank.PlayerUUID = ?");
@@ -263,6 +641,8 @@ AString cRankManager::GetPlayerRankName(const AString & a_PlayerUUID)
AStringVector cRankManager::GetPlayerGroups(const AString & a_PlayerUUID)
{
+ ASSERT(m_IsInitialized);
+
AStringVector res;
try
{
@@ -296,6 +676,8 @@ AStringVector cRankManager::GetPlayerGroups(const AString & a_PlayerUUID)
AStringVector cRankManager::GetPlayerPermissions(const AString & a_PlayerUUID)
{
+ ASSERT(m_IsInitialized);
+
AStringVector res;
try
{
@@ -329,6 +711,8 @@ AStringVector cRankManager::GetPlayerPermissions(const AString & a_PlayerUUID)
AStringVector cRankManager::GetRankGroups(const AString & a_RankName)
{
+ ASSERT(m_IsInitialized);
+
AStringVector res;
try
{
@@ -357,6 +741,8 @@ AStringVector cRankManager::GetRankGroups(const AString & a_RankName)
AStringVector cRankManager::GetGroupPermissions(const AString & a_GroupName)
{
+ ASSERT(m_IsInitialized);
+
AStringVector res;
try
{
@@ -384,6 +770,8 @@ AStringVector cRankManager::GetGroupPermissions(const AString & a_GroupName)
AStringVector cRankManager::GetRankPermissions(const AString & a_RankName)
{
+ ASSERT(m_IsInitialized);
+
AStringVector res;
try
{
@@ -412,6 +800,8 @@ AStringVector cRankManager::GetRankPermissions(const AString & a_RankName)
AStringVector cRankManager::GetAllRanks(void)
{
+ ASSERT(m_IsInitialized);
+
AStringVector res;
try
{
@@ -434,6 +824,8 @@ AStringVector cRankManager::GetAllRanks(void)
AStringVector cRankManager::GetAllGroups(void)
{
+ ASSERT(m_IsInitialized);
+
AStringVector res;
try
{
@@ -456,6 +848,8 @@ AStringVector cRankManager::GetAllGroups(void)
AStringVector cRankManager::GetAllPermissions(void)
{
+ ASSERT(m_IsInitialized);
+
AStringVector res;
try
{
@@ -483,6 +877,8 @@ bool cRankManager::GetPlayerMsgVisuals(
AString & a_MsgNameColorCode
)
{
+ ASSERT(m_IsInitialized);
+
AStringVector res;
try
{
@@ -526,6 +922,8 @@ void cRankManager::AddRank(
const AString & a_MsgNameColorCode
)
{
+ ASSERT(m_IsInitialized);
+
try
{
// Check if such a rank name is already used:
@@ -566,6 +964,8 @@ void cRankManager::AddRank(
void cRankManager::AddGroup(const AString & a_GroupName)
{
+ ASSERT(m_IsInitialized);
+
try
{
// Check if such a rank name is already used:
@@ -603,6 +1003,8 @@ void cRankManager::AddGroup(const AString & a_GroupName)
bool cRankManager::AddGroupToRank(const AString & a_GroupName, const AString & a_RankName)
{
+ ASSERT(m_IsInitialized);
+
try
{
SQLite::Transaction trans(m_DB);
@@ -680,6 +1082,8 @@ bool cRankManager::AddGroupToRank(const AString & a_GroupName, const AString & a
bool cRankManager::AddPermissionToGroup(const AString & a_Permission, const AString & a_GroupName)
{
+ ASSERT(m_IsInitialized);
+
try
{
// Wrapp the entire operation into a transaction:
@@ -746,8 +1150,83 @@ bool cRankManager::AddPermissionToGroup(const AString & a_Permission, const AStr
+bool cRankManager::AddPermissionsToGroup(const AStringVector & a_Permissions, const AString & a_GroupName)
+{
+ ASSERT(m_IsInitialized);
+
+ try
+ {
+ // Wrapp the entire operation into a transaction:
+ SQLite::Transaction trans(m_DB);
+
+ // 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:
+ trans.commit();
+ 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);
+
AStringVector res;
try
{
@@ -823,6 +1302,8 @@ void cRankManager::RemoveRank(const AString & a_RankName, const AString & a_Repl
void cRankManager::RemoveGroup(const AString & a_GroupName)
{
+ ASSERT(m_IsInitialized);
+
try
{
// Wrap everything into a transaction:
@@ -876,6 +1357,8 @@ void cRankManager::RemoveGroup(const AString & a_GroupName)
void cRankManager::RemoveGroupFromRank(const AString & a_GroupName, const AString & a_RankName)
{
+ ASSERT(m_IsInitialized);
+
try
{
// Wrap everything into a transaction:
@@ -930,6 +1413,8 @@ void cRankManager::RemoveGroupFromRank(const AString & a_GroupName, const AStrin
void cRankManager::RemovePermissionFromGroup(const AString & a_Permission, const AString & a_GroupName)
{
+ ASSERT(m_IsInitialized);
+
try
{
// Wrap everything into a transaction:
@@ -972,6 +1457,8 @@ void cRankManager::RemovePermissionFromGroup(const AString & a_Permission, const
bool cRankManager::RenameRank(const AString & a_OldName, const AString & a_NewName)
{
+ ASSERT(m_IsInitialized);
+
try
{
// Wrap everything into a transaction:
@@ -1014,6 +1501,8 @@ bool cRankManager::RenameRank(const AString & a_OldName, const AString & a_NewNa
bool cRankManager::RenameGroup(const AString & a_OldName, const AString & a_NewName)
{
+ ASSERT(m_IsInitialized);
+
try
{
// Wrap everything into a transaction:
@@ -1056,6 +1545,8 @@ bool cRankManager::RenameGroup(const AString & a_OldName, const AString & a_NewN
void cRankManager::SetPlayerRank(const AString & a_PlayerUUID, const AString & a_PlayerName, const AString & a_RankName)
{
+ ASSERT(m_IsInitialized);
+
try
{
// Wrap the entire operation into a transaction:
@@ -1123,6 +1614,8 @@ void cRankManager::SetRankVisuals(
const AString & a_MsgNameColorCode
)
{
+ ASSERT(m_IsInitialized);
+
try
{
SQLite::Statement stmt(m_DB, "UPDATE Rank SET MsgPrefix = ?, MsgSuffix = ?, MsgNameColorCode = ? WHERE Name = ?");
@@ -1152,6 +1645,8 @@ bool cRankManager::GetRankVisuals(
AString & a_MsgNameColorCode
)
{
+ ASSERT(m_IsInitialized);
+
try
{
SQLite::Statement stmt(m_DB, "SELECT MsgPrefix, MsgSuffix, MsgNameColorCode FROM Rank WHERE Name = ?");
@@ -1179,6 +1674,8 @@ bool cRankManager::GetRankVisuals(
bool cRankManager::RankExists(const AString & a_RankName)
{
+ ASSERT(m_IsInitialized);
+
try
{
SQLite::Statement stmt(m_DB, "SELECT * FROM Rank WHERE Name = ?");
@@ -1202,6 +1699,8 @@ bool cRankManager::RankExists(const AString & a_RankName)
bool cRankManager::GroupExists(const AString & a_GroupName)
{
+ ASSERT(m_IsInitialized);
+
try
{
SQLite::Statement stmt(m_DB, "SELECT * FROM PermGroup WHERE Name = ?");
@@ -1225,6 +1724,8 @@ bool cRankManager::GroupExists(const AString & a_GroupName)
bool cRankManager::IsPlayerRankSet(const AString & a_PlayerUUID)
{
+ ASSERT(m_IsInitialized);
+
try
{
SQLite::Statement stmt(m_DB, "SELECT * FROM PlayerRank WHERE PlayerUUID = ?");
@@ -1248,6 +1749,8 @@ bool cRankManager::IsPlayerRankSet(const AString & a_PlayerUUID)
bool cRankManager::IsGroupInRank(const AString & a_GroupName, const AString & a_RankName)
{
+ ASSERT(m_IsInitialized);
+
try
{
SQLite::Statement stmt(m_DB,
@@ -1277,6 +1780,8 @@ bool cRankManager::IsGroupInRank(const AString & a_GroupName, const AString & a_
bool cRankManager::IsPermissionInGroup(const AString & a_Permission, const AString & a_GroupName)
{
+ ASSERT(m_IsInitialized);
+
try
{
SQLite::Statement stmt(m_DB,
@@ -1302,3 +1807,36 @@ bool cRankManager::IsPermissionInGroup(const AString & a_Permission, const AStri
+
+bool cRankManager::AreDBTablesEmpty(void)
+{
+ return (
+ IsDBTableEmpty("Rank") &&
+ IsDBTableEmpty("PlayerRank") &&
+ IsDBTableEmpty("PermGroup") &&
+ IsDBTableEmpty("RankPermGroup") &&
+ IsDBTableEmpty("PermissionItem")
+ );
+}
+
+
+
+
+
+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;
+}
+
+
+
+
diff --git a/src/RankManager.h b/src/RankManager.h
index 0a43bfe5d..ffe7638e1 100644
--- a/src/RankManager.h
+++ b/src/RankManager.h
@@ -14,11 +14,22 @@
+class cMojangAPI;
+
+
+
+
+
class cRankManager
{
public:
+ /** Creates the rank manager. Needs to be initialized before other use. */
cRankManager(void);
+ /** 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. */
AString GetPlayerRankName(const AString & a_PlayerUUID);
@@ -79,6 +90,11 @@ public:
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,
@@ -147,7 +163,19 @@ public:
protected:
+ /** The database storage for all the data. */
SQLite::Database m_DB;
+
+ /** Set to true once the manager is initialized. */
+ bool m_IsInitialized;
+
+
+ /** 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);
} ;
diff --git a/src/Root.cpp b/src/Root.cpp
index c20cf0d21..333d92de5 100644
--- a/src/Root.cpp
+++ b/src/Root.cpp
@@ -156,6 +156,7 @@ void cRoot::Start(void)
m_WebAdmin->Init();
LOGD("Loading settings...");
+ m_RankManager.Initialize(m_MojangAPI);
m_GroupManager = new cGroupManager();
m_CraftingRecipes = new cCraftingRecipes;
m_FurnaceRecipe = new cFurnaceRecipe();