aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortreeston <treeston.mmoc@gmail.com>2016-01-20 23:43:13 +0100
committerjoschiwald <joschiwald.trinity@gmail.com>2016-09-03 13:46:33 +0200
commita92bfb5505e17cc183ec5aa74b01564d566b7677 (patch)
tree874e21eafc1c7bed13881c9119fa4f9fd155253f /src
parenta140ba4b7598636e1e90ed0243b58878231e4b3b (diff)
Scripts/Commands: Small QoL adjustments
- .debug hostil now shows spawn ID (DBGUID) in addition to current GUID (so you can .go creature to it) - .npc temp now takes an additional argument before the creature entry that determines whether the spawned creature instantly despawns upon death. Default is instant despawn (current behavior). - Add .npc evade command. - Add .pet level command. - .server shutdown and .server restart now fail with an error message if time is below a config var (GM.ForceShutdownThreshold, default 30s) as long as another player is connected. - New commands .server shutdown force and .server restart force bypass this limitation. (cherry picked from commit 0e1eb460d687e545f95caf0f38a16c3feb7132a3)
Diffstat (limited to 'src')
-rw-r--r--src/server/game/Accounts/RBAC.h10
-rw-r--r--src/server/game/Miscellaneous/Language.h4
-rw-r--r--src/server/game/World/World.cpp1
-rw-r--r--src/server/game/World/World.h4
-rw-r--r--src/server/scripts/Commands/cs_character.cpp2
-rw-r--r--src/server/scripts/Commands/cs_debug.cpp2
-rw-r--r--src/server/scripts/Commands/cs_npc.cpp117
-rw-r--r--src/server/scripts/Commands/cs_pet.cpp59
-rw-r--r--src/server/scripts/Commands/cs_server.cpp53
-rw-r--r--src/server/worldserver/worldserver.conf.dist7
10 files changed, 193 insertions, 66 deletions
diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h
index ff1bcfa02f5..32906572f03 100644
--- a/src/server/game/Accounts/RBAC.h
+++ b/src/server/game/Accounts/RBAC.h
@@ -741,9 +741,13 @@ enum RBACPermissions
RBAC_PERM_COMMAND_GO_QUEST = 834,
RBAC_PERM_COMMAND_DEBUG_LOADCELLS = 835,
RBAC_PERM_COMMAND_DEBUG_BOUNDARY = 836,
- RBAC_PERM_COMMAND_RELOAD_CHARACTER_TEMPLATE = 837, // RBAC_PERM_COMMAND_NPC_EVADE on 3.3.5a, someone fix it
- RBAC_PERM_COMMAND_RELOAD_QUEST_GREETING = 838, // RBAC_PERM_COMMAND_RELOAD_QUEST_GREETING on 3.3.5a, someone fix it
- RBAC_PERM_COMMAND_DEBUG_SEND_PLAYSCENE = 839, // RBAC_PERM_COMMAND_DEBUG_SEND_PLAYSCENE on 3.3.5a, someone fix it
+ RBAC_PERM_COMMAND_NPC_EVADE = 837,
+ RBAC_PERM_COMMAND_PET_LEVEL = 838,
+ RBAC_PERM_COMMAND_SERVER_SHUTDOWN_FORCE = 839,
+ RBAC_PERM_COMMAND_SERVER_RESTART_FORCE = 840,
+ RBAC_PERM_COMMAND_RELOAD_CHARACTER_TEMPLATE = 841,
+ RBAC_PERM_COMMAND_RELOAD_QUEST_GREETING = 842,
+ RBAC_PERM_COMMAND_DEBUG_SEND_PLAYSCENE = 843,
// custom permissions 1000+
RBAC_PERM_MAX
diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h
index 6f1f4cb2409..8cc13b83a72 100644
--- a/src/server/game/Miscellaneous/Language.h
+++ b/src/server/game/Miscellaneous/Language.h
@@ -1218,6 +1218,8 @@ enum TrinityStrings
LANG_CREATURE_NO_INTERIOR_POINT_FOUND = 11011,
LANG_CREATURE_MOVEMENT_NOT_BOUNDED = 11012,
LANG_CREATURE_MOVEMENT_MAYBE_UNBOUNDED = 11013,
- LANG_INSTANCE_BIND_MISMATCH = 11014
+ LANG_INSTANCE_BIND_MISMATCH = 11014,
+ LANG_CREATURE_NOT_AI_ENABLED = 11015,
+ LANG_SELECT_PLAYER_OR_PET = 11016,
};
#endif
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index c75022cf8e2..1dff6d2c713 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -1034,6 +1034,7 @@ void World::LoadConfigSettings(bool reload)
}
m_bool_configs[CONFIG_ALLOW_GM_GROUP] = sConfigMgr->GetBoolDefault("GM.AllowInvite", false);
m_bool_configs[CONFIG_GM_LOWER_SECURITY] = sConfigMgr->GetBoolDefault("GM.LowerSecurity", false);
+ m_int_configs[CONFIG_FORCE_SHUTDOWN_THRESHOLD] = sConfigMgr->GetIntDefault("GM.ForceShutdownThreshold", 30);
m_int_configs[CONFIG_GROUP_VISIBILITY] = sConfigMgr->GetIntDefault("Visibility.GroupMode", 1);
diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h
index 37687f0eef0..bf6eec03cff 100644
--- a/src/server/game/World/World.h
+++ b/src/server/game/World/World.h
@@ -65,7 +65,8 @@ enum ServerMessageType
enum ShutdownMask
{
SHUTDOWN_MASK_RESTART = 1,
- SHUTDOWN_MASK_IDLE = 2
+ SHUTDOWN_MASK_IDLE = 2,
+ SHUTDOWN_MASK_FORCE = 4
};
enum ShutdownExitCode
@@ -279,6 +280,7 @@ enum WorldIntConfigs
CONFIG_GM_LEVEL_IN_GM_LIST,
CONFIG_GM_LEVEL_IN_WHO_LIST,
CONFIG_START_GM_LEVEL,
+ CONFIG_FORCE_SHUTDOWN_THRESHOLD,
CONFIG_GROUP_VISIBILITY,
CONFIG_MAIL_DELIVERY_DELAY,
CONFIG_UPTIME_UPDATE,
diff --git a/src/server/scripts/Commands/cs_character.cpp b/src/server/scripts/Commands/cs_character.cpp
index b482716e059..96100a1c49e 100644
--- a/src/server/scripts/Commands/cs_character.cpp
+++ b/src/server/scripts/Commands/cs_character.cpp
@@ -433,7 +433,7 @@ public:
if (isalpha(levelStr[0]))
{
nameStr = levelStr;
- levelStr = NULL; // current level will used
+ levelStr = nullptr; // current level will used
}
Player* target;
diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp
index 0956b25dcdc..d27548675b1 100644
--- a/src/server/scripts/Commands/cs_debug.cpp
+++ b/src/server/scripts/Commands/cs_debug.cpp
@@ -832,7 +832,7 @@ public:
if (Unit* unit = ref->GetSource()->GetOwner())
{
++count;
- handler->PSendSysMessage(" %u. %s (%s) - threat %f", count, unit->GetName().c_str(), unit->GetGUID().ToString().c_str(), ref->getThreat());
+ handler->PSendSysMessage(" %u. %s (%s, SpawnId: %u) - threat %f", count, unit->GetName().c_str(), unit->GetGUID().ToString().c_str(), unit->GetTypeId() == TYPEID_UNIT ? unit->ToCreature()->GetSpawnId() : 0, ref->getThreat());
}
ref = ref->next();
}
diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp
index 96acb89f1cb..54eb7867c34 100644
--- a/src/server/scripts/Commands/cs_npc.cpp
+++ b/src/server/scripts/Commands/cs_npc.cpp
@@ -222,14 +222,15 @@ public:
{ "whisper", rbac::RBAC_PERM_COMMAND_NPC_WHISPER, false, &HandleNpcWhisperCommand, "" },
{ "yell", rbac::RBAC_PERM_COMMAND_NPC_YELL, false, &HandleNpcYellCommand, "" },
{ "tame", rbac::RBAC_PERM_COMMAND_NPC_TAME, false, &HandleNpcTameCommand, "" },
- { "add", rbac::RBAC_PERM_COMMAND_NPC_ADD, false, NULL, "", npcAddCommandTable },
- { "delete", rbac::RBAC_PERM_COMMAND_NPC_DELETE, false, NULL, "", npcDeleteCommandTable },
- { "follow", rbac::RBAC_PERM_COMMAND_NPC_FOLLOW, false, NULL, "", npcFollowCommandTable },
- { "set", rbac::RBAC_PERM_COMMAND_NPC_SET, false, NULL, "", npcSetCommandTable },
+ { "add", rbac::RBAC_PERM_COMMAND_NPC_ADD, false, nullptr, "", npcAddCommandTable },
+ { "delete", rbac::RBAC_PERM_COMMAND_NPC_DELETE, false, nullptr, "", npcDeleteCommandTable },
+ { "follow", rbac::RBAC_PERM_COMMAND_NPC_FOLLOW, false, nullptr, "", npcFollowCommandTable },
+ { "set", rbac::RBAC_PERM_COMMAND_NPC_SET, false, nullptr, "", npcSetCommandTable },
+ { "evade", rbac::RBAC_PERM_COMMAND_NPC_EVADE, false, &HandleNpcEvadeCommand, "" },
};
static std::vector<ChatCommand> commandTable =
{
- { "npc", rbac::RBAC_PERM_COMMAND_NPC, false, NULL, "", npcCommandTable },
+ { "npc", rbac::RBAC_PERM_COMMAND_NPC, false, nullptr, "", npcCommandTable },
};
return commandTable;
}
@@ -322,17 +323,17 @@ public:
uint32 itemId = item_int;
- char* fmaxcount = strtok(NULL, " "); //add maxcount, default: 0
+ char* fmaxcount = strtok(nullptr, " "); //add maxcount, default: 0
uint32 maxcount = 0;
if (fmaxcount)
maxcount = atoul(fmaxcount);
- char* fincrtime = strtok(NULL, " "); //add incrtime, default: 0
+ char* fincrtime = strtok(nullptr, " "); //add incrtime, default: 0
uint32 incrtime = 0;
if (fincrtime)
incrtime = atoul(fincrtime);
- char* fextendedcost = strtok(NULL, " "); //add ExtendedCost, default: 0
+ char* fextendedcost = strtok(nullptr, " "); //add ExtendedCost, default: 0
uint32 extendedcost = fextendedcost ? atoul(fextendedcost) : 0;
Creature* vendor = handler->getSelectedCreature();
if (!vendor)
@@ -365,7 +366,7 @@ public:
return false;
char* guidStr = strtok((char*)args, " ");
- char* waitStr = strtok((char*)NULL, " ");
+ char* waitStr = strtok((char*)nullptr, " ");
ObjectGuid::LowType lowGuid = strtoull(guidStr, nullptr, 10);
@@ -450,36 +451,24 @@ public:
}
Creature* creature = handler->getSelectedCreature();
- if (!creature)
+ if (!creature || creature->IsPet())
{
handler->SendSysMessage(LANG_SELECT_CREATURE);
handler->SetSentErrorMessage(true);
return false;
}
- if (creature->IsPet())
- {
- if (((Pet*)creature)->getPetType() == HUNTER_PET)
- {
- creature->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, sObjectMgr->GetXPForLevel(lvl)/4);
- creature->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0);
- }
- ((Pet*)creature)->GivePetLevel(lvl);
- }
- else
- {
- creature->SetMaxHealth(100 + 30*lvl);
- creature->SetHealth(100 + 30*lvl);
- creature->SetLevel(lvl);
- creature->SaveToDB();
- }
+ creature->SetMaxHealth(100 + 30*lvl);
+ creature->SetHealth(100 + 30*lvl);
+ creature->SetLevel(lvl);
+ creature->SaveToDB();
return true;
}
static bool HandleNpcDeleteCommand(ChatHandler* handler, char const* args)
{
- Creature* unit = NULL;
+ Creature* unit = nullptr;
if (*args)
{
@@ -634,7 +623,7 @@ public:
return false;
char* arg1 = strtok((char*)args, " ");
- char* arg2 = strtok((char*)NULL, "");
+ char* arg2 = strtok((char*)nullptr, "");
if (!arg1 || !arg2)
return false;
@@ -948,8 +937,8 @@ public:
// later switched on/off according to special events (like escort
// quests, etc)
char* guid_str = strtok((char*)args, " ");
- char* type_str = strtok((char*)NULL, " ");
- char* dontdel_str = strtok((char*)NULL, " ");
+ char* type_str = strtok((char*)nullptr, " ");
+ char* dontdel_str = strtok((char*)nullptr, " ");
bool doNotDelete = false;
@@ -957,7 +946,7 @@ public:
return false;
ObjectGuid::LowType lowguid = UI64LIT(0);
- Creature* creature = NULL;
+ Creature* creature = nullptr;
if (dontdel_str)
{
@@ -983,7 +972,7 @@ public:
{
//TC_LOG_ERROR("misc", "DEBUG: type_str, NODEL ");
doNotDelete = true;
- type_str = NULL;
+ type_str = nullptr;
}
}
}
@@ -1023,7 +1012,7 @@ public:
}
// now lowguid is low guid really existed creature
- // and creature point (maybe) to this creature or NULL
+ // and creature point (maybe) to this creature or nullptr
MovementGeneratorType move_type;
@@ -1313,7 +1302,7 @@ public:
}
char* receiver_str = strtok((char*)args, " ");
- char* text = strtok(NULL, "");
+ char* text = strtok(nullptr, "");
if (!receiver_str || !text)
{
@@ -1372,7 +1361,16 @@ public:
if (!*args)
return false;
- char* charID = handler->extractKeyFromLink((char*)args, "Hcreature_entry");
+ bool loot = false;
+ char const* spawntype_str = strtok((char*)args, " ");
+ char const* entry_str = strtok(nullptr, "");
+ if (stricmp(spawntype_str, "LOOT") == 0)
+ loot = true;
+ else if (stricmp(spawntype_str, "NOLOOT") == 0)
+ loot = false;
+ else
+ entry_str = args;
+ char* charID = handler->extractKeyFromLink((char*)entry_str, "Hcreature_entry");
if (!charID)
return false;
@@ -1385,7 +1383,7 @@ public:
if (!sObjectMgr->GetCreatureTemplate(id))
return false;
- chr->SummonCreature(id, *chr, TEMPSUMMON_CORPSE_DESPAWN, 120);
+ chr->SummonCreature(id, *chr, loot ? TEMPSUMMON_CORPSE_TIMED_DESPAWN : TEMPSUMMON_CORPSE_DESPAWN, 30 * IN_MILLISECONDS);
return true;
}
@@ -1457,6 +1455,51 @@ public:
return true;
}
+ static bool HandleNpcEvadeCommand(ChatHandler* handler, char const* args)
+ {
+ Creature* creatureTarget = handler->getSelectedCreature();
+ if (!creatureTarget || creatureTarget->IsPet())
+ {
+ handler->PSendSysMessage(LANG_SELECT_CREATURE);
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (!creatureTarget->IsAIEnabled)
+ {
+ handler->PSendSysMessage(LANG_CREATURE_NOT_AI_ENABLED);
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* type_str = args ? strtok((char*)args, " ") : nullptr;
+ char* force_str = args ? strtok(nullptr, " ") : nullptr;
+
+ CreatureAI::EvadeReason why = CreatureAI::EVADE_REASON_OTHER;
+ bool force = false;
+ if (type_str)
+ {
+ if (stricmp(type_str, "NO_HOSTILES") == 0 || stricmp(type_str, "EVADE_REASON_NO_HOSTILES") == 0)
+ why = CreatureAI::EVADE_REASON_NO_HOSTILES;
+ else if (stricmp(type_str, "BOUNDARY") == 0 || stricmp(type_str, "EVADE_REASON_BOUNDARY") == 0)
+ why = CreatureAI::EVADE_REASON_BOUNDARY;
+ else if (stricmp(type_str, "SEQUENCE_BREAK") == 0 || stricmp(type_str, "EVADE_REASON_SEQUENCE_BREAK") == 0)
+ why = CreatureAI::EVADE_REASON_SEQUENCE_BREAK;
+ else if (stricmp(type_str, "FORCE") == 0)
+ force = true;
+
+ if (!force && force_str)
+ if (stricmp(force_str, "FORCE") == 0)
+ force = true;
+ }
+
+ if (force)
+ creatureTarget->ClearUnitState(UNIT_STATE_EVADE);
+ creatureTarget->AI()->EnterEvadeMode(why);
+
+ return true;
+ }
+
static bool HandleNpcAddFormationCommand(ChatHandler* handler, char const* args)
{
if (!*args)
@@ -1568,7 +1611,7 @@ public:
if (!pSlotID)
return false;
- char* pItemID = strtok(NULL, " ");
+ char* pItemID = strtok(nullptr, " ");
if (!pItemID)
return false;
diff --git a/src/server/scripts/Commands/cs_pet.cpp b/src/server/scripts/Commands/cs_pet.cpp
index 3aed932cb4c..df729d6ec4f 100644
--- a/src/server/scripts/Commands/cs_pet.cpp
+++ b/src/server/scripts/Commands/cs_pet.cpp
@@ -22,6 +22,19 @@
#include "ObjectMgr.h"
#include "ScriptMgr.h"
+static inline Pet* GetSelectedPlayerPetOrOwn(ChatHandler* handler)
+{
+ if (Unit* target = handler->getSelectedUnit())
+ {
+ if (target->GetTypeId() == TYPEID_PLAYER)
+ return target->ToPlayer()->GetPet();
+ if (target->IsPet())
+ return target->ToPet();
+ return nullptr;
+ }
+ Player* player = handler->GetSession()->GetPlayer();
+ return player ? player->GetPet() : nullptr;
+}
class pet_commandscript : public CommandScript
{
public:
@@ -34,6 +47,7 @@ public:
{ "create", rbac::RBAC_PERM_COMMAND_PET_CREATE, false, &HandlePetCreateCommand, "" },
{ "learn", rbac::RBAC_PERM_COMMAND_PET_LEARN, false, &HandlePetLearnCommand, "" },
{ "unlearn", rbac::RBAC_PERM_COMMAND_PET_UNLEARN, false, &HandlePetUnlearnCommand, "" },
+ { "level", rbac::RBAC_PERM_COMMAND_PET_LEVEL, false, &HandlePetLevelCommand, "" },
};
static std::vector<ChatCommand> commandTable =
@@ -54,9 +68,9 @@ public:
return false;
}
- CreatureTemplate const* creatrueTemplate = creatureTarget->GetCreatureTemplate();
+ CreatureTemplate const* creatureTemplate = creatureTarget->GetCreatureTemplate();
// Creatures with family 0 crashes the server
- if (!creatrueTemplate->family)
+ if (!creatureTemplate->family)
{
handler->PSendSysMessage("This creature cannot be tamed. (family id: 0).");
handler->SetSentErrorMessage(true);
@@ -119,12 +133,11 @@ public:
if (!*args)
return false;
- Player* player = handler->GetSession()->GetPlayer();
- Pet* pet = player->GetPet();
+ Pet* pet = GetSelectedPlayerPetOrOwn(handler);
if (!pet)
{
- handler->PSendSysMessage("You have no pet");
+ handler->SendSysMessage(LANG_SELECT_PLAYER_OR_PET);
handler->SetSentErrorMessage(true);
return false;
}
@@ -162,11 +175,10 @@ public:
if (!*args)
return false;
- Player* player = handler->GetSession()->GetPlayer();
- Pet* pet = player->GetPet();
+ Pet* pet = GetSelectedPlayerPetOrOwn(handler);
if (!pet)
{
- handler->PSendSysMessage("You have no pet");
+ handler->SendSysMessage(LANG_SELECT_PLAYER_OR_PET);
handler->SetSentErrorMessage(true);
return false;
}
@@ -180,6 +192,37 @@ public:
return true;
}
+
+ static bool HandlePetLevelCommand(ChatHandler* handler, char const* args)
+ {
+ Pet* pet = GetSelectedPlayerPetOrOwn(handler);
+ Player* owner = pet ? pet->GetOwner() : nullptr;
+ if (!pet || !owner)
+ {
+ handler->SendSysMessage(LANG_SELECT_PLAYER_OR_PET);
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ int32 level = args ? atoi(args) : 0;
+ if (level == 0)
+ level = owner->getLevel() - pet->getLevel();
+ if (level == 0 || level < -STRONG_MAX_LEVEL || level > STRONG_MAX_LEVEL)
+ {
+ handler->SendSysMessage(LANG_BAD_VALUE);
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ int32 newLevel = pet->getLevel() + level;
+ if (newLevel < 1)
+ newLevel = 1;
+ else if (newLevel > owner->getLevel())
+ newLevel = owner->getLevel();
+
+ pet->GivePetLevel(newLevel);
+ return true;
+ }
};
void AddSC_pet_commandscript()
diff --git a/src/server/scripts/Commands/cs_server.cpp b/src/server/scripts/Commands/cs_server.cpp
index 9363cbe2b59..3cb90513430 100644
--- a/src/server/scripts/Commands/cs_server.cpp
+++ b/src/server/scripts/Commands/cs_server.cpp
@@ -53,12 +53,14 @@ public:
static std::vector<ChatCommand> serverRestartCommandTable =
{
{ "cancel", rbac::RBAC_PERM_COMMAND_SERVER_RESTART_CANCEL, true, &HandleServerShutDownCancelCommand, "" },
+ { "force", rbac::RBAC_PERM_COMMAND_SERVER_RESTART_FORCE, true, &HandleServerForceRestartCommand, "" },
{ "" , rbac::RBAC_PERM_COMMAND_SERVER_RESTART, true, &HandleServerRestartCommand, "" },
};
static std::vector<ChatCommand> serverShutdownCommandTable =
{
{ "cancel", rbac::RBAC_PERM_COMMAND_SERVER_SHUTDOWN_CANCEL, true, &HandleServerShutDownCancelCommand, "" },
+ { "force", rbac::RBAC_PERM_COMMAND_SERVER_SHUTDOWN_FORCE, true, &HandleServerForceShutDownCommand, "" },
{ "" , rbac::RBAC_PERM_COMMAND_SERVER_SHUTDOWN, true, &HandleServerShutDownCommand, "" },
};
@@ -74,19 +76,19 @@ public:
{
{ "corpses", rbac::RBAC_PERM_COMMAND_SERVER_CORPSES, true, &HandleServerCorpsesCommand, "" },
{ "exit", rbac::RBAC_PERM_COMMAND_SERVER_EXIT, true, &HandleServerExitCommand, "" },
- { "idlerestart", rbac::RBAC_PERM_COMMAND_SERVER_IDLERESTART, true, NULL, "", serverIdleRestartCommandTable },
- { "idleshutdown", rbac::RBAC_PERM_COMMAND_SERVER_IDLESHUTDOWN, true, NULL, "", serverIdleShutdownCommandTable },
+ { "idlerestart", rbac::RBAC_PERM_COMMAND_SERVER_IDLERESTART, true, nullptr, "", serverIdleRestartCommandTable },
+ { "idleshutdown", rbac::RBAC_PERM_COMMAND_SERVER_IDLESHUTDOWN, true, nullptr, "", serverIdleShutdownCommandTable },
{ "info", rbac::RBAC_PERM_COMMAND_SERVER_INFO, true, &HandleServerInfoCommand, "" },
{ "motd", rbac::RBAC_PERM_COMMAND_SERVER_MOTD, true, &HandleServerMotdCommand, "" },
{ "plimit", rbac::RBAC_PERM_COMMAND_SERVER_PLIMIT, true, &HandleServerPLimitCommand, "" },
- { "restart", rbac::RBAC_PERM_COMMAND_SERVER_RESTART, true, NULL, "", serverRestartCommandTable },
- { "shutdown", rbac::RBAC_PERM_COMMAND_SERVER_SHUTDOWN, true, NULL, "", serverShutdownCommandTable },
- { "set", rbac::RBAC_PERM_COMMAND_SERVER_SET, true, NULL, "", serverSetCommandTable },
+ { "restart", rbac::RBAC_PERM_COMMAND_SERVER_RESTART, true, nullptr, "", serverRestartCommandTable },
+ { "shutdown", rbac::RBAC_PERM_COMMAND_SERVER_SHUTDOWN, true, nullptr, "", serverShutdownCommandTable },
+ { "set", rbac::RBAC_PERM_COMMAND_SERVER_SET, true, nullptr, "", serverSetCommandTable },
};
static std::vector<ChatCommand> commandTable =
{
- { "server", rbac::RBAC_PERM_COMMAND_SERVER, true, NULL, "", serverCommandTable },
+ { "server", rbac::RBAC_PERM_COMMAND_SERVER, true, nullptr, "", serverCommandTable },
};
return commandTable;
}
@@ -196,19 +198,34 @@ public:
return true;
}
- static bool HandleServerShutDownCommand(ChatHandler* /*handler*/, char const* args)
+ static inline bool IsOnlyUser(WorldSession* mySession)
{
- return ShutdownServer(args, 0, SHUTDOWN_EXIT_CODE);
+ // check if there is any session connected from a different address
+ std::string myAddr = mySession ? mySession->GetRemoteAddress() : "";
+ SessionMap const& sessions = sWorld->GetAllSessions();
+ for (SessionMap::value_type const& session : sessions)
+ if (session.second && myAddr != session.second->GetRemoteAddress())
+ return false;
+ return true;
+ }
+ static bool HandleServerShutDownCommand(ChatHandler* handler, char const* args)
+ {
+ return ShutdownServer(args, IsOnlyUser(handler->GetSession()) ? SHUTDOWN_MASK_FORCE : 0, SHUTDOWN_EXIT_CODE);
}
- static bool HandleServerRestartCommand(ChatHandler* /*handler*/, char const* args)
+ static bool HandleServerRestartCommand(ChatHandler* handler, char const* args)
{
- return ShutdownServer(args, SHUTDOWN_MASK_RESTART, RESTART_EXIT_CODE);
+ return ShutdownServer(args, IsOnlyUser(handler->GetSession()) ? (SHUTDOWN_MASK_FORCE | SHUTDOWN_MASK_RESTART) : SHUTDOWN_MASK_RESTART, RESTART_EXIT_CODE);
}
- static bool HandleServerIdleRestartCommand(ChatHandler* /*handler*/, char const* args)
+ static bool HandleServerForceShutDownCommand(ChatHandler* /*handler*/, char const* args)
{
- return ShutdownServer(args, SHUTDOWN_MASK_RESTART | SHUTDOWN_MASK_IDLE, RESTART_EXIT_CODE);
+ return ShutdownServer(args, SHUTDOWN_MASK_FORCE, SHUTDOWN_EXIT_CODE);
+ }
+
+ static bool HandleServerForceRestartCommand(ChatHandler* /*handler*/, char const* args)
+ {
+ return ShutdownServer(args, SHUTDOWN_MASK_FORCE | SHUTDOWN_MASK_RESTART, RESTART_EXIT_CODE);
}
static bool HandleServerIdleShutDownCommand(ChatHandler* /*handler*/, char const* args)
@@ -216,6 +233,11 @@ public:
return ShutdownServer(args, SHUTDOWN_MASK_IDLE, SHUTDOWN_EXIT_CODE);
}
+ static bool HandleServerIdleRestartCommand(ChatHandler* /*handler*/, char const* args)
+ {
+ return ShutdownServer(args, SHUTDOWN_MASK_RESTART | SHUTDOWN_MASK_IDLE, RESTART_EXIT_CODE);
+ }
+
// Exit the realm
static bool HandleServerExitCommand(ChatHandler* handler, char const* /*args*/)
{
@@ -260,8 +282,8 @@ public:
return false;
char* type = strtok((char*)args, " ");
- char* name = strtok(NULL, " ");
- char* level = strtok(NULL, " ");
+ char* name = strtok(nullptr, " ");
+ char* level = strtok(nullptr, " ");
if (!type || !name || !level || *name == '\0' || *level == '\0' || (*type != 'a' && *type != 'l'))
return false;
@@ -362,6 +384,9 @@ private:
if (!ParseExitCode(exitCodeStr, exitCode))
return false;
+ if (delay < (int32)sWorld->getIntConfig(CONFIG_FORCE_SHUTDOWN_THRESHOLD) && !(shutdownMask & SHUTDOWN_MASK_FORCE))
+ return false;
+
sWorld->ShutdownServ(delay, shutdownMask, static_cast<uint8>(exitCode), std::string(reason));
return true;
diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist
index 88ac39bdc93..3f90d848d24 100644
--- a/src/server/worldserver/worldserver.conf.dist
+++ b/src/server/worldserver/worldserver.conf.dist
@@ -1919,6 +1919,13 @@ GM.AllowInvite = 0
GM.LowerSecurity = 0
#
+# GM.ForceShutdownThreshold
+# Description: Minimum shutdown time in seconds before 'force' is required if other players are connected.
+# Default: 30
+
+GM.ForceShutdownThreshold = 30
+
+#
###################################################################################################
###################################################################################################