Merge pull request #19 from SWG-Source/master

3.1 Branch Sync
This commit is contained in:
John
2021-08-17 13:30:12 -04:00
committed by GitHub
19 changed files with 363 additions and 111 deletions
+1 -2
View File
@@ -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 &params)
{
/*
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 &params)
{
/*
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;
}
@@ -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);
}
@@ -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
@@ -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);
}
}