mirror of
https://github.com/SWG-Source/src.git
synced 2026-07-13 21:01:08 -04:00
+1
-2
@@ -117,8 +117,7 @@ elseif (UNIX)
|
||||
-Wno-write-strings -Wno-unknown-pragmas \
|
||||
-Wno-uninitialized -Wno-reorder -Wno-tautological-constant-out-of-range-compare")
|
||||
|
||||
# if you'd like to opt in to our statistics, remove "-DSTELLA_INTERNAL" and your server will send us a heartbeat at startup
|
||||
add_definitions(-DLINUX -D_REENTRANT -Dlinux -D_USING_STL -D_GNU_SOURCE -D_XOPEN_SOURCE=500 -U_FORTIFY_SOURCE -DSTELLA_INTERNAL)
|
||||
add_definitions(-DLINUX -D_REENTRANT -Dlinux -D_USING_STL -D_GNU_SOURCE -D_XOPEN_SOURCE=500 -U_FORTIFY_SOURCE)
|
||||
|
||||
# this is so some profile specific stuff is turned on in the code
|
||||
if (${CMAKE_BUILD_TYPE} STREQUAL "RELWITHDEBINFO")
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <LoginEnumCluster::ClusterData> 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<StationId, ClientConnection *>(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;
|
||||
|
||||
@@ -3392,9 +3392,27 @@ static void commandFuncOpenContainer(Command const & cmd, NetworkId const &actor
|
||||
return;
|
||||
}
|
||||
|
||||
//-- don't open factory crates
|
||||
if (container->getGameObjectType() == SharedObjectTemplate::GOT_misc_factory_crate)
|
||||
//-- don't open factory crates or crafting tools
|
||||
const int got = container->getGameObjectType();
|
||||
if (got == SharedObjectTemplate::GOT_misc_factory_crate || got == SharedObjectTemplate::GOT_tool_crafting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//-- if we are requesting to open an inventory, datapad, or bank, make sure it belongs to us or we're a CSR
|
||||
//-- this provides better security for /editDatapad /editInventory and /editBank admin commands.
|
||||
const uint32 crc = container->getTemplateCrc();
|
||||
if((player->isPlayerControlled()) && (crc == 2007924155 || crc == -1783727815 || crc == -172438875))
|
||||
{
|
||||
Client * const clientObj = player->getClient();
|
||||
if(clientObj)
|
||||
{
|
||||
if(container->getOwnerId() != actor && !clientObj->isGod())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-- if they are opening a crafting station, what they really want is the hopper
|
||||
//-- but only if the object is not a volume container
|
||||
@@ -7929,6 +7947,10 @@ static void commandFuncSquelch(Command const &, NetworkId const &actor, NetworkI
|
||||
const Client * gmClient = gm->getClient();
|
||||
if (gmClient)
|
||||
{
|
||||
if(!gmClient->isGod())
|
||||
{
|
||||
return;
|
||||
}
|
||||
char buffer[2048];
|
||||
if (!target.isValid())
|
||||
{
|
||||
@@ -7998,6 +8020,10 @@ static void commandFuncUnsquelch(Command const &, NetworkId const &actor, Networ
|
||||
const Client * gmClient = gm->getClient();
|
||||
if (gmClient)
|
||||
{
|
||||
if(!gmClient->isGod())
|
||||
{
|
||||
return;
|
||||
}
|
||||
char buffer[2048];
|
||||
if (!target.isValid())
|
||||
{
|
||||
@@ -8067,6 +8093,10 @@ static void commandFuncGrantWarden(Command const &, NetworkId const &actor, Netw
|
||||
const Client * gmClient = gm->getClient();
|
||||
if (gmClient)
|
||||
{
|
||||
if(!gmClient->isGod())
|
||||
{
|
||||
return;
|
||||
}
|
||||
char buffer[2048];
|
||||
if (!target.isValid())
|
||||
{
|
||||
@@ -8138,6 +8168,10 @@ static void commandFuncRevokeWarden(Command const &, NetworkId const &actor, Net
|
||||
const Client * gmClient = gm->getClient();
|
||||
if (gmClient)
|
||||
{
|
||||
if(!gmClient->isGod())
|
||||
{
|
||||
return;
|
||||
}
|
||||
char buffer[2048];
|
||||
if (!target.isValid())
|
||||
{
|
||||
@@ -8210,16 +8244,16 @@ static void commandFuncSpammer(Command const &, NetworkId const &actor, NetworkI
|
||||
const Client * gmClient = gm->getClient();
|
||||
if (gmClient)
|
||||
{
|
||||
// check warden permission first
|
||||
// check warden permission first or if god
|
||||
const PlayerObject * gmPlayerObject = PlayerCreatureController::getPlayerObject(gm);
|
||||
if (!gmPlayerObject || !gmPlayerObject->isWarden())
|
||||
if (!gmPlayerObject || (!gmPlayerObject->isWarden()) && !gmClient->isGod())
|
||||
{
|
||||
Chat::sendSystemMessage(*gm, StringId("warden", "not_authorized"), Unicode::emptyString);
|
||||
return;
|
||||
}
|
||||
|
||||
// check to see if warden functionality is enabled
|
||||
if (!ConfigServerGame::getEnableWarden())
|
||||
// check to see if warden functionality is enabled but don't restrict if god
|
||||
if (!ConfigServerGame::getEnableWarden() && !gmClient->isGod())
|
||||
{
|
||||
Chat::sendSystemMessage(*gm, StringId("warden", "warden_functionality_disabled"), Unicode::emptyString);
|
||||
return;
|
||||
@@ -8304,6 +8338,16 @@ static void commandFuncSpammer(Command const &, NetworkId const &actor, NetworkI
|
||||
}
|
||||
}
|
||||
|
||||
// can't spammer someone in god mode or another warden
|
||||
if(so->getClient()->isGod() || p->isWarden())
|
||||
{
|
||||
ProsePackage prosePackage;
|
||||
prosePackage.stringId = StringId("warden", "cannot_spammer_target");
|
||||
prosePackage.target.str = so->getAssignedObjectName();
|
||||
Chat::sendSystemMessage(*gm, prosePackage);
|
||||
return;
|
||||
}
|
||||
|
||||
// cannot /spammer self
|
||||
if (target == actor)
|
||||
{
|
||||
@@ -8351,9 +8395,9 @@ static void commandFuncUnspammer(Command const &, NetworkId const &actor, Networ
|
||||
const Client * gmClient = gm->getClient();
|
||||
if (gmClient)
|
||||
{
|
||||
// check warden permission first
|
||||
// check warden permission first or if god
|
||||
const PlayerObject * gmPlayerObject = PlayerCreatureController::getPlayerObject(gm);
|
||||
if (!gmPlayerObject || !gmPlayerObject->isWarden())
|
||||
if (!gmPlayerObject || (!gmPlayerObject->isWarden()) && !gmClient->isGod())
|
||||
{
|
||||
Chat::sendSystemMessage(*gm, StringId("warden", "not_authorized"), Unicode::emptyString);
|
||||
return;
|
||||
@@ -8431,8 +8475,8 @@ static void commandFuncUnspammer(Command const &, NetworkId const &actor, Networ
|
||||
return;
|
||||
}
|
||||
|
||||
// can only /unspammer target that I /spammer(ed)
|
||||
if (p->getSquelchedById() != actor)
|
||||
// can only /unspammer target that I /spammer(ed) unless I'm god
|
||||
if (p->getSquelchedById() != actor && !gmClient->isGod())
|
||||
{
|
||||
ProsePackage prosePackage;
|
||||
prosePackage.stringId = StringId("warden", "cannot_unspammer_target");
|
||||
@@ -8458,8 +8502,16 @@ static void commandFuncUnspammer(Command const &, NetworkId const &actor, Networ
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @deprecated This ungodly idea to empower wardens to grant warden permissions to other players is horrible
|
||||
* and I don't believe this functionality should remotely exist at all absent intentional implementation
|
||||
* by a specific server. GMs can use /grantWarden if they want a Warden or the grant can be scripted.
|
||||
*
|
||||
* Aconite - SWG Source - 2021
|
||||
*/
|
||||
static void commandFuncDeputizeWarden(Command const &, NetworkId const &actor, NetworkId const & target, Unicode::String const ¶ms)
|
||||
{
|
||||
/*
|
||||
const CreatureObject * gm = dynamic_cast<CreatureObject *>(NetworkIdManager::getObjectById(actor));
|
||||
if (gm)
|
||||
{
|
||||
@@ -8531,12 +8583,18 @@ static void commandFuncDeputizeWarden(Command const &, NetworkId const &actor, N
|
||||
"C++DeputizeWardenRspCannotDeputize");
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @see commandFuncDeputizeWarden
|
||||
*/
|
||||
static void commandFuncUndeputizeWarden(Command const &, NetworkId const &actor, NetworkId const & target, Unicode::String const ¶ms)
|
||||
{
|
||||
/*
|
||||
const CreatureObject * gm = dynamic_cast<CreatureObject *>(NetworkIdManager::getObjectById(actor));
|
||||
if (gm)
|
||||
{
|
||||
@@ -8594,6 +8652,7 @@ static void commandFuncUndeputizeWarden(Command const &, NetworkId const &actor,
|
||||
"C++UndeputizeWardenRspCannotUndeputize");
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
@@ -475,6 +475,7 @@ static const CommandParser::CmdInfo cmds[] =
|
||||
{"reloadSwgTcgAccountFeatureId", 1, "<oid>", "Retrieve the SWG TCG account feature Ids for the specified character"},
|
||||
{"unloadBuildingContents", 1, "<oid>", "Unload the contents of a player-placed authoritative demand-loaded building"},
|
||||
{"isOnSolidFloor", 1, "<oid>", "Checks to see if the specified object is \"on solid floor\""},
|
||||
{"proxyToAllGameServers", 1, "<oid>", "Creates a Proxy of the given object on all Game Servers."},
|
||||
|
||||
{"", 0, "", ""} // this must be last
|
||||
};
|
||||
@@ -4204,6 +4205,21 @@ bool ConsoleCommandParserObject::performParsing2(const NetworkId & userId, const
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
else if (isAbbrev(argv[0], "proxyToAllGameServers"))
|
||||
{
|
||||
NetworkId const oid(Unicode::wideToNarrow(argv[1]));
|
||||
auto * const object = dynamic_cast<ServerObject *>(NetworkIdManager::getObjectById(oid));
|
||||
GameServer &gs = GameServer::getInstance();
|
||||
if(object) {
|
||||
gs.createProxyOnAllServers(object);
|
||||
result += getErrorMessage(argv[0], ERR_SUCCESS);
|
||||
} else {
|
||||
result += getErrorMessage(argv[0], ERR_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
else
|
||||
{
|
||||
result += getErrorMessage(argv[0], ERR_NO_HANDLER);
|
||||
|
||||
@@ -195,6 +195,7 @@ static const CommandParser::CmdInfo cmds[] =
|
||||
{"getCommoditiesAuctionLocationPriorityQueue", 1, "<count>", "Gets the top <count> items in the auction location priority queue"},
|
||||
{"getCommoditiesItemTypeMap", 1, "<game object type>", "For the specified game object type, gets the list of item type"},
|
||||
{"setCompletedTutorial", 1, "<oid>", "Mark the account belonging to the specified character (character does not have to be logged in) as having completed the tutorial, so that the skip tutorial option will be available during character creation for that account"},
|
||||
{"getUniverseProcess", 0, "", "Returns the Game Server Universe Process ID"},
|
||||
#ifdef _DEBUG
|
||||
{"setExtraDelayPerFrameMs", 1, "<ms>", "Do an intentional sleep each frame, to emulate long loop time"},
|
||||
{"serverinfo", 0, "", "Serverinformation"},
|
||||
@@ -3449,6 +3450,11 @@ bool ConsoleCommandParserServer::performParsing2(const NetworkId & userId, const
|
||||
result += Unicode::narrowToWide(FormattedString<512>().sprintf("There is no character on this galaxy with id (%s)\n", oid.getValueString().c_str()));
|
||||
}
|
||||
}
|
||||
else if (isAbbrev(argv[0], "getUniverseProcess"))
|
||||
{
|
||||
result += Unicode::narrowToWide(FormattedString<512>().sprintf("Universe Process ID is: %d\n", ServerUniverse::getInstance().getUniverseProcess()));
|
||||
result += getErrorMessage(argv[0], ERR_SUCCESS);
|
||||
}
|
||||
#ifdef _DEBUG
|
||||
else if (isAbbrev(argv[0], "setExtraDelayPerFrameMs"))
|
||||
{
|
||||
|
||||
@@ -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,134 @@ 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<CreatureObject *>(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<CreatureObject *>(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);
|
||||
|
||||
m_godLevel = 0;
|
||||
m_godMode = false;
|
||||
m_godValidated = false;
|
||||
// reset observers and notify cells of potential change
|
||||
CellPermissions::ViewerChangeObserver o(creatureObject);
|
||||
ObserveTracker::onGodModeChanged(*this);
|
||||
// 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 +2252,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");
|
||||
|
||||
|
||||
@@ -105,6 +105,8 @@ public:
|
||||
|
||||
bool isUsingAdminLogin() const;
|
||||
|
||||
void setUsingAdminLogin(bool value);
|
||||
|
||||
CombatDataTable::CombatSpamFilterType getCombatSpamFilter() const;
|
||||
|
||||
int getCombatSpamRangeSquaredFilter() const;
|
||||
|
||||
@@ -18,6 +18,10 @@
|
||||
#include "sharedFoundation/FormattedString.h"
|
||||
#include "sharedGame/ProsePackage.h"
|
||||
#include "sharedGame/ProsePackageParticipant.h"
|
||||
#include "serverScript/GameScriptObject.h"
|
||||
#include "serverScript/ScriptDictionary.h"
|
||||
#include "serverScript/ScriptFunctionTable.h"
|
||||
#include "serverScript/ScriptParameters.h"
|
||||
#include "sharedLog/Log.h"
|
||||
#include "sharedNetworkMessages/ChatAvatarId.h"
|
||||
#include "sharedNetworkMessages/ChatOnRequestLog.h"
|
||||
@@ -331,10 +335,18 @@ void ReportManager::handleMessage(ChatOnRequestLog const &chatOnRequestLog)
|
||||
report.append(reportData.m_clientData);
|
||||
}
|
||||
|
||||
if (!reportData.m_reportLogChannel.empty())
|
||||
LOGU("CustomerService", (reportData.m_reportLogChannel.c_str()), report);
|
||||
else
|
||||
LOGU("CustomerService", ("Report:"), report);
|
||||
//if (!reportData.m_reportLogChannel.empty())
|
||||
// LOGU("CustomerService", (reportData.m_reportLogChannel.c_str()), report);
|
||||
//else
|
||||
// LOGU("CustomerService", ("Report:"), report);
|
||||
|
||||
if(reportingCreatureObject->getScriptObject())
|
||||
{
|
||||
ScriptParams p;
|
||||
p.addParam(reportData.m_harassingNetworkId);
|
||||
p.addParam(report);
|
||||
IGNORE_RETURN(reportingCreatureObject->getScriptObject()->trigAllScripts(Scripting::TRIG_ON_PLAYER_REPORTED_CHAT, p));
|
||||
}
|
||||
|
||||
// System message for success
|
||||
|
||||
|
||||
@@ -662,6 +662,7 @@ void ServerBuildoutManagerNamespace::loadArea(AreaInfo &areaInfo)
|
||||
|
||||
std::set< int64 > objects;
|
||||
|
||||
int64 originalObjId = 0;
|
||||
for (int buildoutRow = 0; buildoutRow < buildoutRowCount; ++buildoutRow)
|
||||
{
|
||||
int64 objId = 0;
|
||||
@@ -726,7 +727,8 @@ void ServerBuildoutManagerNamespace::loadArea(AreaInfo &areaInfo)
|
||||
}
|
||||
else
|
||||
{
|
||||
objId = areaBuildoutTable.getIntValue(objIdColumn, buildoutRow);
|
||||
originalObjId = areaBuildoutTable.getIntValue(objIdColumn, buildoutRow);
|
||||
objId = originalObjId;
|
||||
containerId = areaBuildoutTable.getIntValue(containerColumn, buildoutRow);
|
||||
|
||||
// with new buildout files, the object id is a random 31-bit negative value
|
||||
@@ -776,6 +778,10 @@ void ServerBuildoutManagerNamespace::loadArea(AreaInfo &areaInfo)
|
||||
|
||||
if (containerId == 0 || objects.find(containerId) != objects.end())
|
||||
{
|
||||
// store original ObjId from buildout table as ObjVar on item for reference in-game if needed
|
||||
std::string objVars = areaBuildoutTable.getStringValue(objvarsColumn, buildoutRow);
|
||||
objVars.insert(0, "buildoutObjectId|0|" + std::to_string(originalObjId) + "|");
|
||||
|
||||
areaInfo.buildoutRows.push_back(
|
||||
BuildoutRow(
|
||||
objId,
|
||||
@@ -784,7 +790,7 @@ void ServerBuildoutManagerNamespace::loadArea(AreaInfo &areaInfo)
|
||||
transform_p,
|
||||
serverTemplate,
|
||||
areaBuildoutTable.getStringValue(scriptsColumn, buildoutRow),
|
||||
areaBuildoutTable.getStringValue(objvarsColumn, buildoutRow),
|
||||
objVars,
|
||||
eventRequired,
|
||||
requiredLoadLevel));
|
||||
}
|
||||
|
||||
@@ -1963,9 +1963,6 @@ void ServerObject::serverObjectEndBaselines(bool fromDatabase)
|
||||
// If the object was created authoritative, trigger if needed
|
||||
if (isAuthoritative())
|
||||
triggerMadeAuthoritative();
|
||||
|
||||
if (markedNoTradeRemovable())
|
||||
getScriptObject()->attachScript("item.special.no_trade_removable", true);
|
||||
}
|
||||
|
||||
if (isPlayerControlled())
|
||||
@@ -2566,7 +2563,13 @@ bool ServerObject::serverObjectInitializeFirstTimeObject(ServerObject *cell, Tra
|
||||
|
||||
int count = newTemplate->getScriptsCount();
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
getScriptObject()->attachScript(newTemplate->getScripts(i), true);
|
||||
}
|
||||
if (markedNoTradeRemovable())
|
||||
{
|
||||
getScriptObject()->attachScript("item.special.no_trade_removable", true);
|
||||
}
|
||||
|
||||
if (cell)
|
||||
{
|
||||
|
||||
@@ -330,6 +330,7 @@ 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"},
|
||||
@@ -379,6 +380,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
|
||||
|
||||
@@ -480,6 +480,9 @@ 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 = 306,
|
||||
TRIG_ON_SET_GOD_MODE_OFF = 307,
|
||||
TRIG_LAST_TRIGGER
|
||||
};
|
||||
|
||||
|
||||
@@ -481,7 +481,7 @@ jstring JNICALL ScriptMethodsPlanetNamespace::getBuildoutAreaName( JNIEnv *const
|
||||
return 0;
|
||||
}
|
||||
|
||||
const BuildoutArea * const area = SharedBuildoutAreaManager::findBuildoutAreaAtPosition(sceneId.c_str(), x, z, false );
|
||||
const BuildoutArea * const area = SharedBuildoutAreaManager::findBuildoutAreaAtPosition(sceneId, x, z, false, false);
|
||||
|
||||
return area ? JavaString( area->areaName ).getReturnValue() : 0;
|
||||
}
|
||||
|
||||
+7
-3
@@ -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);
|
||||
}
|
||||
|
||||
+10
-1
@@ -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<bool> m_canLogin;
|
||||
Archive::AutoVariable<bool> m_canCreateRegularCharacter;
|
||||
Archive::AutoVariable<bool> m_canCreateJediCharacter;
|
||||
Archive::AutoVariable<bool> m_canSkipTutorial;
|
||||
Archive::AutoVariable<bool> 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
|
||||
|
||||
+15
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user