diff --git a/engine/server/application/ConnectionServer/src/shared/ClientConnection.cpp b/engine/server/application/ConnectionServer/src/shared/ClientConnection.cpp index 54d2d0b7..187997ed 100755 --- a/engine/server/application/ConnectionServer/src/shared/ClientConnection.cpp +++ b/engine/server/application/ConnectionServer/src/shared/ClientConnection.cpp @@ -394,19 +394,41 @@ ClientConnection::onIdValidated(bool canLogin, bool canCreateRegularCharacter, b // Save lists of claimed rewards, which won't be used again until later in the login sequence m_consumedRewardEvents = consumedRewardEvents; m_claimedRewardItems = claimedRewardItems; + bool isAdmin = false; - - int level = 0; - if (AdminAccountManager::isAdminAccount(Unicode::toLower(getAccountName()), level) && (level != - 0)) // Note: not checking IP, so that owners of god accounts can create characters to play from home without having to erase the characters they use for work + // Revised - SWG Source - 2021 (Aconite) + // Determine if this account is in the Admin Table + if(AdminAccountManager::getAdminLevel(Unicode::toLower(getAccountName())) > 0) { + // Determine if we are using Secure Login (IP restricted) + if(ConfigConnectionServer::getUseSecureLoginForGodAccess()) + { + // If we *require* secure login, and we are *not* from a secure IP, disconnect the client + // as they should not be inside this account + if(!AdminAccountManager::isInternalIp(getRemoteAddress())) + { + LOG("GodMode", ("[%s : %s : %s : %s] login to this admin account failed because secure login was required but the remote IP address connected was not in the allowed range.", + getAccountName().c_str(), getCharacterName().c_str(), getCharacterId().getValueString().c_str(), getRemoteAddress().c_str())); + disconnect(); + return; + } + else + { + m_isSecure = true; + } + } + // If we're here, we're an admin account, and we have the correct IP (if applicable) + // so we're overriding the ClientPermissionMessage values canLogin = true; canCreateRegularCharacter = true; canSkipTutorial = true; m_isAdminAccount = true; + isAdmin = true; // this flag goes to the client to let it know we can connect to a Locked Cluster + LOG("GodMode", ("[%s : %s : %s : %s] validating this account to the Connection Server after successful authentication.", + getAccountName().c_str(), getCharacterName().c_str(), getCharacterId().getValueString().c_str(), getRemoteAddress().c_str())); } - ClientPermissionsMessage c(canLogin, canCreateRegularCharacter, canCreateJediCharacter, canSkipTutorial); + ClientPermissionsMessage c(canLogin, canCreateRegularCharacter, canCreateJediCharacter, canSkipTutorial, isAdmin); send(c, true); DEBUG_REPORT_LOG(true, ("Permissions for %lu:\n", getSUID())); diff --git a/engine/server/application/ConnectionServer/src/shared/ConfigConnectionServer.cpp b/engine/server/application/ConnectionServer/src/shared/ConfigConnectionServer.cpp index 538e2c01..2eef9f77 100755 --- a/engine/server/application/ConnectionServer/src/shared/ConfigConnectionServer.cpp +++ b/engine/server/application/ConnectionServer/src/shared/ConfigConnectionServer.cpp @@ -117,6 +117,8 @@ void ConfigConnectionServer::install(void) KEY_STRING (altPublicBindAddress, ""); KEY_BOOL (useOldSuidGenerator, false); + KEY_BOOL (useSecureLoginForGodAccess, false); + int index = 0; char const * result = 0; do diff --git a/engine/server/application/ConnectionServer/src/shared/ConfigConnectionServer.h b/engine/server/application/ConnectionServer/src/shared/ConfigConnectionServer.h index 7d5edc42..67741268 100755 --- a/engine/server/application/ConnectionServer/src/shared/ConfigConnectionServer.h +++ b/engine/server/application/ConnectionServer/src/shared/ConfigConnectionServer.h @@ -68,6 +68,8 @@ public: int connectionServerNumber; int fakeBuddyPoints; + bool useSecureLoginForGodAccess; + bool useOldSuidGenerator; const char *altPublicBindAddress; @@ -187,6 +189,9 @@ public: static const char *getPublicBindAddress(); static bool getUseOldSuidGenerator(); + + static bool getUseSecureLoginForGodAccess(); + private: static Data *data; }; @@ -480,4 +485,9 @@ inline bool ConfigConnectionServer::getUseOldSuidGenerator() { return data->useOldSuidGenerator; } +inline bool ConfigConnectionServer::getUseSecureLoginForGodAccess() +{ + return data->useSecureLoginForGodAccess; +} + #endif // _ConfigConnectionServer_H diff --git a/engine/server/application/LoginServer/src/shared/LoginServer.cpp b/engine/server/application/LoginServer/src/shared/LoginServer.cpp index 677ad357..bd053af2 100755 --- a/engine/server/application/LoginServer/src/shared/LoginServer.cpp +++ b/engine/server/application/LoginServer/src/shared/LoginServer.cpp @@ -943,10 +943,10 @@ LoginServer::validateAccount(const StationId &stationId, uint32 clusterId, uint3 bool clientIsInternal = false; ClientConnection *conn = getValidatedClient(stationId); if (conn) { - clientIsInternal = AdminAccountManager::isInternalIp(conn->getRemoteAddress()); + clientIsInternal = conn->getAdminLevel() > 0; } - if (clientIsInternal && ConfigLoginServer::getInternalBypassOnlineLimit()) { + if (clientIsInternal) { canLogin = true; } else if (cle->m_numPlayers <= cle->m_onlinePlayerLimit) { canLogin = true; @@ -968,6 +968,12 @@ LoginServer::validateAccount(const StationId &stationId, uint32 clusterId, uint3 canSkipTutorial = true; } + // double check locked cluster + if(cle->m_locked) + { + canLogin = clientIsInternal; + } + ValidateAccountReplyMessage msg(stationId, canLogin, canCreateRegular, canCreateJedi, canSkipTutorial, track, consumedRewardEvents, claimedRewardItems); cle->m_centralServerConnection->send(msg, true); } @@ -1228,32 +1234,8 @@ LoginServer::onValidateClient(StationId suid, const std::string &username, Clien NOT_NULL(conn); WARNING_STRICT_FATAL(getValidatedClient(suid), ("Validating an already valid client in onValidateClient(). StationId: %d UserName: %s", suid, username.c_str())); - int adminLevel = 0; - const bool isAdminAccount = AdminAccountManager::isAdminAccount(Unicode::toLower(username), adminLevel); - - if (conn->getRequestedAdminSuid() != 0) { - //verify internal, secure, is on the god list - bool loginOK = false; - if (!isSecure) { - LOG("CustomerService", ("AdminLogin: User %s (account %li) attempted to log into account %li, but was not using a SecureID token", username.c_str(), suid, conn->getRequestedAdminSuid())); - } else { - if (!AdminAccountManager::isInternalIp(conn->getRemoteAddress())) { - LOG("CustomerService", ("AdminLogin: User %s (account %li) attempted to log into account %li, but was not logging in from an internal IP", username.c_str(), suid, conn->getRequestedAdminSuid())); - } else { - if (!isAdminAccount || adminLevel < 10) { - LOG("CustomerService", ("AdminLogin: User %s (account %li) attempted to log into account %li, but did not have sufficient permissions", username.c_str(), suid, conn->getRequestedAdminSuid())); - } else { - suid = conn->getRequestedAdminSuid(); - loginOK = true; - } - } - } - - if (!loginOK) { - conn->disconnect(); - return; - } - } + // determine if this is an admin account + const bool isAdminAccount = AdminAccountManager::getAdminLevel(username) > 0; // encrypt the clients credentials with the key, return // the cipher text to the client for use as a connection @@ -1297,14 +1279,11 @@ LoginServer::onValidateClient(StationId suid, const std::string &username, Clien conn->send(k, true); delete[] keyBuffer; - // send cluster enum - bool clientInternal = AdminAccountManager::isInternalIp(conn->getRemoteAddress()); - std::vector data; for (ClusterListType::const_iterator j = m_clusterList.begin(); j != m_clusterList.end(); ++j) { ClusterListEntry *cle = *j; - if (cle && cle->m_clusterId != 0 && cle->m_clusterName.size() != 0 && (clientInternal || !cle->m_secret)) { + if (cle && cle->m_clusterId != 0 && cle->m_clusterName.size() != 0 && (isAdminAccount || !cle->m_secret)) { LoginEnumCluster::ClusterData item; item.m_clusterId = cle->m_clusterId; item.m_clusterName = cle->m_clusterName; @@ -1331,7 +1310,7 @@ LoginServer::onValidateClient(StationId suid, const std::string &username, Clien conn->setIsValidated(true); conn->setStationId(suid); conn->setIsSecure(isSecure); - conn->setAdminLevel(isAdminAccount ? adminLevel : -1); + conn->setAdminLevel(isAdminAccount ? AdminAccountManager::getAdminLevel(username) : -1); IGNORE_RETURN(m_validatedClientMap.insert(std::pair(suid, conn))); //Must be done after setting various information in the connection object above @@ -1408,11 +1387,16 @@ void LoginServer::sendExtendedClusterInfo(ClientConnection &client) const { /** * Send the list of active clusters to a client. * Also picks a connection server for the client to use. - * @todo We'd like to resend this to all clients whenever the status - * of any servers changes + * + * Note: *** should send to individual clients - NOT all connected + * clients due to admin permissions check *** */ void LoginServer::sendClusterStatus(ClientConnection &conn) const { - const bool clientIsPrivate = AdminAccountManager::isInternalIp(conn.getRemoteAddress()); + + // Validate admin level here as clientIsPrivate. We don't need to check secure/IP because the + // connection server will force disconnect non-secure connections if they are required by configuration. + const bool clientIsPrivate = conn.getAdminLevel() > 0; + const unsigned int subscriptionBits = conn.getSubscriptionBits(); const bool isFreeTrialAccount = (((subscriptionBits & ClientSubscriptionFeature::FreeTrial) != 0) && ((subscriptionBits & ClientSubscriptionFeature::Base) == 0)); @@ -1451,10 +1435,7 @@ void LoginServer::sendClusterStatus(ClientConnection &conn) const { if (item.m_connectionServerPort) { item.m_connectionServerPingPort = connServer.pingPort; - // for security/confidential information issue, only report - // population count to secured internal connections with - // admin privilege >= 10 - if (clientIsPrivate && conn.getIsSecure() && (conn.getAdminLevel() >= 10)) { + if (clientIsPrivate) { item.m_populationOnline = cle->m_numPlayers; } else { item.m_populationOnline = -1; @@ -1493,10 +1474,14 @@ void LoginServer::sendClusterStatus(ClientConnection &conn) const { } else { item.m_status = LoginClusterStatus::ClusterData::S_loading; } - if (cle->m_locked && !clientIsPrivate) { + if (cle->m_locked) { item.m_status = LoginClusterStatus::ClusterData::S_locked; } // locked takes precedence over up or loading + // flag as admin/secret (see LoginClusterStatus.h) + item.m_isAdmin = clientIsPrivate; + item.m_isSecret = cle->m_secret; + item.m_dontRecommend = (cle->m_notRecommendedDatabase || cle->m_notRecommendedCentral); item.m_onlinePlayerLimit = cle->m_onlinePlayerLimit; item.m_onlineFreeTrialLimit = cle->m_onlineFreeTrialLimit; diff --git a/engine/server/library/serverGame/src/shared/core/Client.cpp b/engine/server/library/serverGame/src/shared/core/Client.cpp index 7d327942..79bc4f56 100755 --- a/engine/server/library/serverGame/src/shared/core/Client.cpp +++ b/engine/server/library/serverGame/src/shared/core/Client.cpp @@ -501,6 +501,12 @@ void Client::addControlledObject(ServerObject &object) { setGodMode(true); } } + + // validate isUsingAdminLogin each onClientReady() call per SWG Source change - 2021 (Aconite) + // isUsingAdminLogin is used to check if an *account* is in the admin table + // so we can monitor admin accessed accounts *regardless* of their current god mode/level + setUsingAdminLogin(AdminAccountManager::getAdminLevel(getAccountName()) > 0); + } // ---------------------------------------------------------------------- @@ -1937,53 +1943,131 @@ float Client::computeDeltaTimeInSeconds(uint32 const syncStampLong) const { //----------------------------------------------------------------------- +/** + * setGodMode + * Processes the request to turn God Mode in the Client on, + * enabling admin commands and admin-treatment of a CreatureObject. + * + * @param value true = god mode on; false = god mode off + * @return true if successful, false if not + * + * Handling was rewritten by SWG Source - 2021 for applicability + * to Source/VM context + * Authors: Aconite + */ bool Client::setGodMode(bool value) { - // (re?) check god permissions - m_godLevel = AdminAccountManager::getAdminLevel(m_accountName.c_str()); - if (ConfigServerGame::getAdminGodToAll() || (m_godLevel > 0)) { - m_godValidated = true; - if (ConfigServerGame::getAdminGodToAll()) { - m_godLevel = ConfigServerGame::getAdminGodToAllGodLevel(); - } - } + const bool wasInGodMode = m_godMode; + auto * creatureObject = safe_cast(m_primaryControlledObject.getObject()); + ScriptParams params; - bool wasInGodMode = m_godMode; - m_godMode = value; - - if (value && !m_godValidated) { - LOG("CustomerService", ("Avatar:%s denied god mode because it wasn't validated.", PlayerObject::getAccountDescription(getCharacterObjectId()).c_str())); - m_godMode = false; - } - - CreatureObject *primaryControlledObject = safe_cast(m_primaryControlledObject.getObject()); - if (!primaryControlledObject) { - if (value && !wasInGodMode) { - LOG("CustomerService", ("Avatar:%s denied god mode because it has no associated character.", m_accountName.c_str())); - } - m_godMode = false; + if(!creatureObject) + { + LOG("GodMode", ("[%s : %s : %s : %s] /setGodMode failed because we couldn't get the CreatureObject.", + getAccountName().c_str(), getCharacterName().c_str(), getCharacterObjectId().getValueString().c_str(), getIpAddress().c_str())); return false; } - if (m_godMode) { - IGNORE_RETURN(primaryControlledObject->grantCommand(AdminAccountManager::getAdminCommandName(), false)); - } else { - primaryControlledObject->revokeCommand(AdminAccountManager::getAdminCommandName(), false, true); - } - - if (m_godMode != wasInGodMode) { - if (m_godMode) { - LOG("CustomerService", ("Avatar:%s granted god mode %s level %d.", PlayerObject::getAccountDescription(getCharacterObjectId()).c_str(), AdminAccountManager::getAdminCommandName(), m_godLevel)); - } else { - LOG("CustomerService", ("Avatar:%s dropped god mode.", PlayerObject::getAccountDescription(getCharacterObjectId()).c_str())); + // Request to turn God Mode Off, in which case we don't need to validate permissions, because off isn't bad + if(!value) + { + // If we were already in god mode, then we've already made an authenticated request to be in god mode, + // so log the event and proceed as an authorized activity, unless a script trigger blocks the switch. + if(wasInGodMode) + { + // allow a SCRIPT_OVERRIDE to block turning god mode off if necessary + // see OnTurnedGodModeOff in script.player.base_player.java + if(creatureObject->getScriptObject()->trigAllScripts(Scripting::TRIG_ON_SET_GOD_MODE_OFF, params) != SCRIPT_CONTINUE) + { + LOG("GodMode", ("[%s : %s : %s : %s] /setGodMode off failed because OnSetGodModeOff did not return SCRIPT_CONTINUE.", + getAccountName().c_str(), getCharacterName().c_str(), getCharacterObjectId().getValueString().c_str(), getIpAddress().c_str())); + return false; + } + else + { + LOG("GodMode", ("[%s : %s : %s : %s] /setGodMode off success.", + getAccountName().c_str(), getCharacterName().c_str(), getCharacterObjectId().getValueString().c_str(), getIpAddress().c_str())); + } } - - // cell permissions may change for us for all cells, so observe all buildings in range for the change - CellPermissions::ViewerChangeObserver o(primaryControlledObject); - - ObserveTracker::onGodModeChanged(*this); + m_godLevel = 0; + m_godMode = false; + m_godValidated = false; + // remove the characterAbility "admin" so GM commands aren't sent by the client while not in God Mode + IGNORE_RETURN(creatureObject->revokeCommand(AdminAccountManager::getAdminCommandName(), false, true)); + return true; } - return (value == m_godMode); // return true if the value was set to what was requested, false otherwise + + // If we're here, it's because the request is to turn God Mode ON + // First, determine if we're using Secure Login Mode (IP-restricted) + if(ConfigServerGame::getUseSecureLoginForGodAccess()) + { + if(!AdminAccountManager::isInternalIp(getIpAddress())) + { + LOG("GodMode", ("[%s : %s : %s : %s] /setGodMode on failed because secure login was required and the connection IP was not approved.", + getAccountName().c_str(), getCharacterName().c_str(), getCharacterObjectId().getValueString().c_str(), getIpAddress().c_str())); + return false; + } + } + + // If we aren't required to be secure, or if we are and we are secure, begin permission checks + // Start with if everyone can have God Mode, in which case we don't need to bother otherwise + int godLevel = 0; + if(ConfigServerGame::getAdminGodToAll() && ConfigServerGame::getAdminGodToAllGodLevel() > 0) + { + // Make sure the account isn't in the admin table, because that should supersede + // whatever the adminGodToAll level is. + if(AdminAccountManager::getAdminLevel(m_accountName) == 0) + { + godLevel = ConfigServerGame::getAdminGodToAllGodLevel(); + } + else + { + godLevel = AdminAccountManager::getAdminLevel(m_accountName); + } + } + // We aren't giving god to everyone, so validate actual god level permissions + else + { + godLevel = AdminAccountManager::getAdminLevel(m_accountName); + } + // Grant God Mode + if(godLevel > 0) + { + // Allow a SCRIPT_OVERRIDE to block turning God Mode on via OnSetGodModeOn trigger + // see script.player.player_base.java + if(creatureObject->getScriptObject()->trigAllScripts(Scripting::TRIG_ON_SET_GOD_MODE_ON, params) != SCRIPT_CONTINUE) + { + LOG("GodMode", ("[%s : %s : %s : %s] /setGodMode on failed because OnSetGodModeOn did not return SCRIPT_CONTINUE.", + getAccountName().c_str(), getCharacterName().c_str(), getCharacterObjectId().getValueString().c_str(), getIpAddress().c_str())); + return false; + } + else + { + // flags + m_godMode = true; + m_godValidated = true; + m_godLevel = godLevel; + + // grant all commands with the "admin" characterAbility + IGNORE_RETURN(creatureObject->grantCommand(AdminAccountManager::getAdminCommandName(), false)); + + // reset observers and notify cells of potential change + CellPermissions::ViewerChangeObserver o(creatureObject); + ObserveTracker::onGodModeChanged(*this); + + LOG("GodMode", ("[%s : %s : %s : %s] /setGodMode on success.", + getAccountName().c_str(), getCharacterName().c_str(), getCharacterObjectId().getValueString().c_str(), getIpAddress().c_str())); + return true; + } + } + // If we're here, it's because someone tried to use /setGod who doesn't have permissions + else + { + LOG("GodMode", ("[%s : %s : %s : %s] /setGodMode on failed because the requesting account does not have permissions.", + getAccountName().c_str(), getCharacterName().c_str(), getCharacterObjectId().getValueString().c_str(), getIpAddress().c_str())); + return false; + } + } //----------------------------------------------------------------------- @@ -2165,12 +2249,20 @@ void Client::launchWebBrowser(std::string const &url) const { // ---------------------------------------------------------------------- -bool Client::isUsingAdminLogin() const { +bool Client::isUsingAdminLogin() const +{ return m_usingAdminLogin; } // ---------------------------------------------------------------------- +void Client::setUsingAdminLogin(bool value) +{ + m_usingAdminLogin = value; +} + +// ---------------------------------------------------------------------- + bool Client::shouldReceiveCombatSpam(NetworkId const &attacker, Vector const &attackerPosition_w, NetworkId const &defender, Vector const &defenderPosition_w) const { PROFILER_AUTO_BLOCK_DEFINE("Client::shouldReceiveCombatSpam"); diff --git a/engine/server/library/serverGame/src/shared/core/Client.h b/engine/server/library/serverGame/src/shared/core/Client.h index e495059b..9fcf1724 100755 --- a/engine/server/library/serverGame/src/shared/core/Client.h +++ b/engine/server/library/serverGame/src/shared/core/Client.h @@ -105,6 +105,8 @@ public: bool isUsingAdminLogin() const; + void setUsingAdminLogin(bool value); + CombatDataTable::CombatSpamFilterType getCombatSpamFilter() const; int getCombatSpamRangeSquaredFilter() const; diff --git a/engine/server/library/serverScript/src/shared/ScriptFunctionTable.cpp b/engine/server/library/serverScript/src/shared/ScriptFunctionTable.cpp index 48f8bdbe..b9d91ae4 100755 --- a/engine/server/library/serverScript/src/shared/ScriptFunctionTable.cpp +++ b/engine/server/library/serverScript/src/shared/ScriptFunctionTable.cpp @@ -330,7 +330,6 @@ static const Scripting::ScriptFuncTable ScriptFuncList[] = {Scripting::TRIG_ENTER_REGION, "OnEnterRegion", "ss"}, {Scripting::TRIG_EXIT_REGION, "OnExitRegion", "ss"}, - {Scripting::TRIG_ON_PLAYER_REPORTED_CHAT, "OnPlayerReportedChat", "Ou"}, //---------------------------------------------------------------------- {Scripting::TRIG_VENDOR_ITEM_COUNT_REPLY, "OnVendorItemCountReply", "ii"}, @@ -380,6 +379,9 @@ static const Scripting::ScriptFuncTable ScriptFuncList[] = {Scripting::TRIG_PVP_RANKING_CHANGED, "OnPvpRankingChanged", "ii"}, + {Scripting::TRIG_ON_SET_GOD_MODE_ON, "OnSetGodModeOn", ""}, + {Scripting::TRIG_ON_SET_GOD_MODE_OFF, "OnSetGodModeOff", ""}, + //----------------------------------------------------------------------- //-- buff builder diff --git a/engine/server/library/serverScript/src/shared/ScriptFunctionTable.h b/engine/server/library/serverScript/src/shared/ScriptFunctionTable.h index 262233eb..19cead31 100755 --- a/engine/server/library/serverScript/src/shared/ScriptFunctionTable.h +++ b/engine/server/library/serverScript/src/shared/ScriptFunctionTable.h @@ -480,7 +480,8 @@ enum TrigId TRIG_ON_ABANDON_PLAYER_QUEST = 302, TRIG_ON_GCW_SCORE_CATEGORY_PERCENTILE_CHANGE = 303, TRIG_WAYPOINT_WARP = 304, - TRIG_ON_PLAYER_REPORTED_CHAT = 305, + TRIG_ON_SET_GOD_MODE_ON = 305, + TRIG_ON_SET_GOD_MODE_OFF = 306, TRIG_LAST_TRIGGER }; diff --git a/engine/shared/library/sharedNetworkMessages/src/shared/clientGameServer/ClientPermissionsMessage.cpp b/engine/shared/library/sharedNetworkMessages/src/shared/clientGameServer/ClientPermissionsMessage.cpp index 8e3c0aea..7d4652f5 100755 --- a/engine/shared/library/sharedNetworkMessages/src/shared/clientGameServer/ClientPermissionsMessage.cpp +++ b/engine/shared/library/sharedNetworkMessages/src/shared/clientGameServer/ClientPermissionsMessage.cpp @@ -10,17 +10,19 @@ // ====================================================================== -ClientPermissionsMessage::ClientPermissionsMessage(bool canLogin, bool canCreateRegularCharacter, bool canCreateJediCharacter, bool canSkipTutorial) : +ClientPermissionsMessage::ClientPermissionsMessage(bool canLogin, bool canCreateRegularCharacter, bool canCreateJediCharacter, bool canSkipTutorial, bool isAdmin) : GameNetworkMessage("ClientPermissionsMessage"), m_canLogin(canLogin), m_canCreateRegularCharacter(canCreateRegularCharacter), m_canCreateJediCharacter(canCreateJediCharacter), - m_canSkipTutorial(canSkipTutorial) + m_canSkipTutorial(canSkipTutorial), + m_isAdmin(isAdmin) { addVariable(m_canLogin); addVariable(m_canCreateRegularCharacter); addVariable(m_canCreateJediCharacter); addVariable(m_canSkipTutorial); + addVariable(m_isAdmin); } //----------------------------------------------------------------------- @@ -30,12 +32,14 @@ ClientPermissionsMessage::ClientPermissionsMessage(Archive::ReadIterator & sourc m_canLogin(), m_canCreateRegularCharacter(), m_canCreateJediCharacter(), - m_canSkipTutorial() + m_canSkipTutorial(), + m_isAdmin() { addVariable(m_canLogin); addVariable(m_canCreateRegularCharacter); addVariable(m_canCreateJediCharacter); addVariable(m_canSkipTutorial); + addVariable(m_isAdmin); unpack(source); } diff --git a/engine/shared/library/sharedNetworkMessages/src/shared/clientGameServer/ClientPermissionsMessage.h b/engine/shared/library/sharedNetworkMessages/src/shared/clientGameServer/ClientPermissionsMessage.h index 8ad6c9da..374a63d8 100755 --- a/engine/shared/library/sharedNetworkMessages/src/shared/clientGameServer/ClientPermissionsMessage.h +++ b/engine/shared/library/sharedNetworkMessages/src/shared/clientGameServer/ClientPermissionsMessage.h @@ -24,7 +24,7 @@ class ClientPermissionsMessage : public GameNetworkMessage { public: - ClientPermissionsMessage(bool canLogin, bool canCreateRegularCharacter, bool canCreateJediCharacter, bool canSkipTutorial); + ClientPermissionsMessage(bool canLogin, bool canCreateRegularCharacter, bool canCreateJediCharacter, bool canSkipTutorial, bool isAdmin); explicit ClientPermissionsMessage(Archive::ReadIterator & source); virtual ~ClientPermissionsMessage(); @@ -33,12 +33,14 @@ class ClientPermissionsMessage : public GameNetworkMessage bool getCanCreateRegularCharacter() const; bool getCanCreateJediCharacter() const; bool getCanSkipTutorial() const; + bool getIsAdmin() const; private: Archive::AutoVariable m_canLogin; Archive::AutoVariable m_canCreateRegularCharacter; Archive::AutoVariable m_canCreateJediCharacter; Archive::AutoVariable m_canSkipTutorial; + Archive::AutoVariable m_isAdmin; ClientPermissionsMessage(); ClientPermissionsMessage(const ClientPermissionsMessage&); @@ -73,6 +75,13 @@ inline bool ClientPermissionsMessage::getCanSkipTutorial() const return m_canSkipTutorial.get(); } +// ---------------------------------------------------------------------- + +inline bool ClientPermissionsMessage::getIsAdmin() const +{ + return m_isAdmin.get(); +} + // ====================================================================== #endif // _INCLUDED_ClientCentralMessages_H diff --git a/engine/shared/library/sharedNetworkMessages/src/shared/clientLoginServer/LoginClusterStatus.h b/engine/shared/library/sharedNetworkMessages/src/shared/clientLoginServer/LoginClusterStatus.h index 6c074132..69897510 100755 --- a/engine/shared/library/sharedNetworkMessages/src/shared/clientLoginServer/LoginClusterStatus.h +++ b/engine/shared/library/sharedNetworkMessages/src/shared/clientLoginServer/LoginClusterStatus.h @@ -31,6 +31,17 @@ struct LoginClusterStatus_ClusterData bool m_dontRecommend; uint32 m_onlinePlayerLimit; uint32 m_onlineFreeTrialLimit; + + // This flag tells the client that the account which requested the cluster information + // is considered an admin by the server (in Admin Account Table). This enables connection + // to locked clusters and viewing secret clusters. We don't need to worry about this flag + // being manipulated because the Connection Server verifies admin permissions after the fact. + // This flag merely unblocks client-side user interface elements for locked/secret clusters. + // We also send this flag in the ClientPermissionsMessage header for character creation. + bool m_isAdmin; + // Flag so we can append (Secret) in Client (for admins) + bool m_isSecret; + }; /** @@ -85,6 +96,8 @@ namespace Archive get(source,c.m_dontRecommend); get(source,c.m_onlinePlayerLimit); get(source,c.m_onlineFreeTrialLimit); + get(source, c.m_isAdmin); + get(source,c.m_isSecret); } inline void put(ByteStream & target, const LoginClusterStatus_ClusterData &c) @@ -101,6 +114,8 @@ namespace Archive put(target,c.m_dontRecommend); put(target,c.m_onlinePlayerLimit); put(target,c.m_onlineFreeTrialLimit); + put(target,c.m_isAdmin); + put(target,c.m_isSecret); } }