Files
src-1.2/engine/server/library/serverGame/src/shared/console/ConsoleCommandParserAi.cpp
T

676 lines
24 KiB
C++
Executable File

// ======================================================================
//
// ConsoleCommandParserAi.cpp
// Copyright (c) 2004 Sony Online Entertainment
//
// ======================================================================
#include "serverGame/FirstServerGame.h"
#include "serverGame/ConsoleCommandParserAi.h"
#include "serverGame/AiCreatureController.h"
#include "serverGame/AiShipController.h"
#include "serverGame/Chat.h"
#include "serverGame/CreatureObject.h"
#include "serverGame/HateList.h"
#include "serverGame/ServerWorld.h"
#include "serverGame/WeaponObject.h"
#include "serverUtility/ServerClock.h"
#include "sharedFile/FileNameUtils.h"
#include "sharedFoundation/FormattedString.h"
#include "sharedFoundation/NetworkId.h"
#include "sharedGame/AiDebugString.h"
#include "sharedNetworkMessages/GenericValueTypeMessage.h"
#include "sharedObject/CellProperty.h"
#include "sharedObject/NetworkIdManager.h"
#include "sharedUtility/DataTable.h"
#include "sharedUtility/DataTableManager.h"
#include "UnicodeUtils.h"
// ======================================================================
static const CommandParser::CmdInfo cmds[] =
{
{"info", 0, "[oid]", "Displays information about the specified AI. Assumes target if no oid specified."},
{"hatelist", 0, "[oid]", "Displays the specified object's hate list. Assumes target if no oid specified."},
{"kill", 0, "[oid]", "Kills the specified AI. Assumes target if no oid specified."},
{"log", 0, "[oid]", "Toggles log file data for the specified AI. Assumes target if no oid specified."},
{"retreat", 0, "[oid]", "Toggles whether the specified AI can auto retreat due to a player running away without attacking. Assumes target if no oid specified."},
{"walkspeed", 1, "<speed | oid speed>", "Displays the current walk speed or sets the walk speed for the specified AI. Assumes target if no oid specified."},
{"runspeed", 1, "<speed | oid speed>", "Displays the current run speed or sets the run speed for the specified AI. Assumes target if no oid specified."},
#ifdef _DEBUG
{"killall", 0, "", "Kills all the AI on the server."},
{"debug", 0, "", "Toggles client AI debugging."},
{"debugText", 0, "", "Toggles the client AI debugging text."},
#endif // _DEBUG
{"", 0, "", ""} // this must be last
};
// ======================================================================
//
// ConsoleCommandParserAiNamespace
//
// ======================================================================
namespace ConsoleCommandParserAiNamespace
{
CreatureObject * const getAiCreatureObject(CreatureObject const & user, std::vector<Unicode::String> const & argv);
}
//-----------------------------------------------------------------
CreatureObject * const ConsoleCommandParserAiNamespace::getAiCreatureObject(CreatureObject const & user, std::vector<Unicode::String> const & argv)
{
NetworkId aiNetworkId;
if (argv.size() > 1)
{
aiNetworkId = NetworkId(Unicode::wideToNarrow(argv[1]));
}
else
{
aiNetworkId = user.getLookAtTarget();
}
CreatureObject * const aiCreatureObject = CreatureObject::getCreatureObject(aiNetworkId);
if ( (aiCreatureObject == nullptr)
|| aiCreatureObject->isPlayerControlled())
{
Chat::sendSystemMessage(user, Unicode::narrowToWide(FormattedString<1024>().sprintf("[FAILURE] Unable to resolve %s to an AI", aiNetworkId.getValueString().c_str())), Unicode::emptyString);
}
return aiCreatureObject;
}
using namespace ConsoleCommandParserAiNamespace;
// ======================================================================
//
// ConsoleCommandParserAi
//
// ======================================================================
//-----------------------------------------------------------------
ConsoleCommandParserAi::ConsoleCommandParserAi()
: CommandParser("ai", 0, "...", "Ai related commands.", 0)
{
createDelegateCommands (cmds);
}
//-----------------------------------------------------------------
bool ConsoleCommandParserAi::performParsing(const NetworkId & userId, const StringVector_t & argv, const String_t & originalCommand, String_t & result, const CommandParser * node)
{
NOT_NULL (node);
UNREF (userId);
UNREF(originalCommand);
FormattedString<1024> fs;
if (isAbbrev(argv[0], "log"))
{
CreatureObject * const userCreatureObject = CreatureObject::getCreatureObject(userId);
if (userCreatureObject == nullptr)
{
result += getErrorMessage(argv[0], ERR_FAIL);
}
else
{
CreatureObject * const aiCreatureObject = getAiCreatureObject(*userCreatureObject, argv);
if (aiCreatureObject == nullptr)
{
result += getErrorMessage(argv[0], ERR_FAIL);
}
else
{
AICreatureController * const aiCreatureController = AICreatureController::asAiCreatureController(aiCreatureObject->getController());
if (aiCreatureController == nullptr)
{
Chat::sendSystemMessage(*userCreatureObject, Unicode::narrowToWide(fs.sprintf("[FAILURE] Unable to resolve %s to an AI", aiCreatureObject->getNetworkId().getValueString().c_str())), Unicode::emptyString);
result += getErrorMessage(argv[0], ERR_FAIL);
}
else
{
aiCreatureController->setLogging(!aiCreatureController->isLogging());
Chat::sendSystemMessage(*userCreatureObject, Unicode::narrowToWide(fs.sprintf("[%s] Ai logging %s", (aiCreatureController->isLogging() ? "ENABLED" : "DISABLED"), aiCreatureObject->getNetworkId().getValueString().c_str())), Unicode::emptyString);
result += getErrorMessage(argv[0], ERR_SUCCESS);
}
}
}
}
else if (isAbbrev(argv[0], "info"))
{
CreatureObject * const userCreatureObject = CreatureObject::getCreatureObject(userId);
if (userCreatureObject == nullptr)
{
result += getErrorMessage(argv[0], ERR_FAIL);
}
else
{
NetworkId targetNetworkId;
if (argv.size () > 1)
{
targetNetworkId = NetworkId(Unicode::wideToNarrow(argv[1]));
}
else
{
targetNetworkId = userCreatureObject->getLookAtTarget();
}
AICreatureController * const aiCreatureController = AICreatureController::getAiCreatureController(targetNetworkId);
if (aiCreatureController == nullptr)
{
result = Unicode::narrowToWide(fs.sprintf("[FAILURE] Unable to resolve %s to an AI\n", targetNetworkId.getValueString().c_str()));
result += getErrorMessage(argv[0], ERR_FAIL);
}
else
{
CreatureObject const * const creatureOwner = aiCreatureController->getCreature();
result += Unicode::narrowToWide(fs.sprintf("\nobj_id: %s\n", aiCreatureController->getCreature()->getNetworkId().getValueString().c_str()));
result += Unicode::narrowToWide(fs.sprintf("creatureName: %s\n", aiCreatureController->getCreatureName().getString()));
result += Unicode::narrowToWide(fs.sprintf("level: %i\n", aiCreatureController->getCreature()->getLevel()));
// World position
{
Vector const & position_w = creatureOwner->getPosition_w();
result += Unicode::narrowToWide(fs.sprintf("world position: pos(%.1f, %.1f, %.1f)\n", position_w.x, position_w.y, position_w.z));
}
// Cell position
{
CellProperty const * const cellProperty = creatureOwner->getParentCell();
if (cellProperty != nullptr)
{
Vector const & position_p = creatureOwner->getPosition_p();
result += Unicode::narrowToWide(fs.sprintf("cell location: pos(%.1f, %.1f, %.1f) cell(%s:%s)\n", position_p.x, position_p.y, position_p.z, cellProperty->getOwner().getNetworkId().getValueString().c_str(), cellProperty->getCellName()));
}
}
Location const & location = aiCreatureController->getHomeLocation();
result += Unicode::narrowToWide(fs.sprintf("homeLocation: pos(%.1f, %.1f, %.1f) cell(%s:%s) scene(%s)\n", location.getCoordinates().x, location.getCoordinates().y, location.getCoordinates().z, location.getCell().getValueString().c_str(), (location.getCell() == NetworkId::cms_invalid) ? ":world" : "", location.getSceneId()));
result += Unicode::narrowToWide(fs.sprintf("invulnerable: %s\n", aiCreatureController->getCreature()->isInvulnerable() ? "yes" : "no"));
result += Unicode::narrowToWide(fs.sprintf("hibernating: %s\n", aiCreatureController->getHibernate() ? "yes" : "no"));
result += Unicode::narrowToWide(fs.sprintf("movement state: %s\n", AiMovementBase::getMovementString(aiCreatureController->getMovementType())));
result += Unicode::narrowToWide(fs.sprintf("walk speed(%.1f)\n", creatureOwner->getWalkSpeed()));
result += Unicode::narrowToWide(fs.sprintf("run speed(%.1f) modifier(%.2f)\n", creatureOwner->getRunSpeed(), aiCreatureController->getMovementSpeedPercent()));
if (aiCreatureController->isAggressive())
{
result += Unicode::narrowToWide(fs.sprintf("aggressive: yes @ %.1fm\n", aiCreatureController->getAggroRadius()));
}
else
{
result += Unicode::narrowToWide("aggressive: no\n");
}
if (aiCreatureController->isAssist())
{
result += Unicode::narrowToWide(fs.sprintf("assist: yes @ %.1fm\n", aiCreatureController->getAssistRadius()));
}
else
{
result += Unicode::narrowToWide("assist: no\n");
}
result += Unicode::narrowToWide(fs.sprintf("killer: %s\n", aiCreatureController->isKiller() ? "yes" : "no"));
// Hate List
{
HateList::SortedList hateList;
aiCreatureController->getCreature()->getSortedHateList(hateList);
if (hateList.empty())
{
result += Unicode::narrowToWide(fs.sprintf("* NO HATE TARGETS *\n"));
}
else
{
result += Unicode::narrowToWide(fs.sprintf("* HATE LIST *\n"));
HateList::SortedList::const_iterator iterHateList = hateList.begin();
for (; iterHateList != hateList.end(); ++iterHateList)
{
CachedNetworkId const & hateTarget = iterHateList->first;
TangibleObject const * const hateTargetTangibleObject = TangibleObject::asTangibleObject(hateTarget.getObject());
float const hate = iterHateList->second;
std::string hateTargetName;
if (hateTargetTangibleObject != nullptr)
{
hateTargetName = Unicode::wideToNarrow(hateTargetTangibleObject->getEncodedObjectName()).c_str();
}
else
{
hateTargetName = "nullptr";
}
result += Unicode::narrowToWide(fs.sprintf("%s:%s(%.1f)\n", hateTargetName.c_str(), hateTarget.getValueString().c_str(), hate));
}
}
}
// Hate Over Time List
{
std::map<NetworkId, std::pair<float, std::pair<unsigned long, unsigned long> > > const & hateOverTime = aiCreatureController->getCreature()->getHateOverTime();
if (!hateOverTime.empty())
{
result += Unicode::narrowToWide(fs.sprintf("* HATE OVER TIME LIST currentGameTime=(%lu)*\n", ServerClock::getInstance().getGameTimeSeconds()));
for (std::map<NetworkId, std::pair<float, std::pair<unsigned long, unsigned long> > >::const_iterator iter = hateOverTime.begin(); iter != hateOverTime.end(); ++iter)
{
CachedNetworkId const hateTarget(iter->first);
TangibleObject const * const hateTargetTangibleObject = TangibleObject::asTangibleObject(hateTarget.getObject());
std::string hateTargetName;
if (hateTargetTangibleObject != nullptr)
{
hateTargetName = Unicode::wideToNarrow(hateTargetTangibleObject->getEncodedObjectName()).c_str();
}
else
{
hateTargetName = "nullptr";
}
result += Unicode::narrowToWide(fs.sprintf("%s:%s (%.2f, %lu, %lu)\n", hateTargetName.c_str(), hateTarget.getValueString().c_str(), iter->second.first, iter->second.second.first, iter->second.second.second));
}
}
}
result += Unicode::narrowToWide(aiCreatureController->getCombatActionsString());
result += getErrorMessage(argv[0], ERR_SUCCESS);
}
}
}
else if (isAbbrev(argv[0], "hatelist"))
{
CreatureObject * const userCreatureObject = CreatureObject::getCreatureObject(userId);
if (userCreatureObject == nullptr)
{
result += getErrorMessage(argv[0], ERR_FAIL);
}
else
{
NetworkId targetNetworkId;
if (argv.size () > 1)
{
targetNetworkId = NetworkId(Unicode::wideToNarrow(argv[1]));
}
else
{
targetNetworkId = userCreatureObject->getLookAtTarget();
}
TangibleObject const * const to = TangibleObject::asTangibleObject(NetworkIdManager::getObjectById(targetNetworkId));
if (to == nullptr)
{
result += Unicode::narrowToWide(fs.sprintf("Target %s not found or not TangibleObject\n", targetNetworkId.getValueString().c_str()));
}
else
{
result += Unicode::narrowToWide(fs.sprintf("Displaying hate list for %s\n", targetNetworkId.getValueString().c_str()));
// Hate List
{
HateList::SortedList hateList;
to->getSortedHateList(hateList);
if (hateList.empty())
{
result += Unicode::narrowToWide(fs.sprintf("* NO HATE TARGETS *\n"));
}
else
{
result += Unicode::narrowToWide(fs.sprintf("* HATE LIST *\n"));
HateList::SortedList::const_iterator iterHateList = hateList.begin();
for (; iterHateList != hateList.end(); ++iterHateList)
{
CachedNetworkId const & hateTarget = iterHateList->first;
TangibleObject const * const hateTargetTangibleObject = TangibleObject::asTangibleObject(hateTarget.getObject());
float const hate = iterHateList->second;
std::string hateTargetName;
if (hateTargetTangibleObject != nullptr)
{
hateTargetName = Unicode::wideToNarrow(hateTargetTangibleObject->getEncodedObjectName()).c_str();
}
else
{
hateTargetName = "nullptr";
}
result += Unicode::narrowToWide(fs.sprintf("%s:%s(%.1f)\n", hateTargetName.c_str(), hateTarget.getValueString().c_str(), hate));
}
}
}
// Hate Over Time List
{
std::map<NetworkId, std::pair<float, std::pair<unsigned long, unsigned long> > > const & hateOverTime = to->getHateOverTime();
if (!hateOverTime.empty())
{
result += Unicode::narrowToWide(fs.sprintf("* HATE OVER TIME LIST currentGameTime=(%lu)*\n", ServerClock::getInstance().getGameTimeSeconds()));
for (std::map<NetworkId, std::pair<float, std::pair<unsigned long, unsigned long> > >::const_iterator iter = hateOverTime.begin(); iter != hateOverTime.end(); ++iter)
{
CachedNetworkId const hateTarget(iter->first);
TangibleObject const * const hateTargetTangibleObject = TangibleObject::asTangibleObject(hateTarget.getObject());
std::string hateTargetName;
if (hateTargetTangibleObject != nullptr)
{
hateTargetName = Unicode::wideToNarrow(hateTargetTangibleObject->getEncodedObjectName()).c_str();
}
else
{
hateTargetName = "nullptr";
}
result += Unicode::narrowToWide(fs.sprintf("%s:%s (%.2f, %lu, %lu)\n", hateTargetName.c_str(), hateTarget.getValueString().c_str(), iter->second.first, iter->second.second.first, iter->second.second.second));
}
}
}
result += getErrorMessage(argv[0], ERR_SUCCESS);
}
}
}
else if (isAbbrev(argv[0], "kill"))
{
CreatureObject * const userCreatureObject = CreatureObject::getCreatureObject(userId);
if (userCreatureObject == nullptr)
{
result += getErrorMessage(argv[0], ERR_FAIL);
}
else
{
CreatureObject * const aiCreatureObject = getAiCreatureObject(*userCreatureObject, argv);
if (aiCreatureObject == nullptr)
{
result += getErrorMessage(argv[0], ERR_FAIL);
}
else
{
aiCreatureObject->setIncapacitated(true, NetworkId::cms_invalid);
Chat::sendSystemMessage(*userCreatureObject, Unicode::narrowToWide(fs.sprintf("[SUCCESS] AI %s terminated", aiCreatureObject->getNetworkId().getValueString().c_str())), Unicode::emptyString);
result += getErrorMessage(argv[0], ERR_SUCCESS);
}
}
}
else if (isAbbrev(argv[0], "retreat"))
{
CreatureObject * const userCreatureObject = CreatureObject::getCreatureObject(userId);
if (userCreatureObject == nullptr)
{
result += getErrorMessage(argv[0], ERR_FAIL);
}
else
{
CreatureObject * const aiCreatureObject = getAiCreatureObject(*userCreatureObject, argv);
if (aiCreatureObject == nullptr)
{
result += getErrorMessage(argv[0], ERR_FAIL);
}
else
{
aiCreatureObject->setHateListAutoExpireTargetEnabled(!aiCreatureObject->isHateListAutoExpireTargetEnabled());
Chat::sendSystemMessage(*userCreatureObject, Unicode::narrowToWide(fs.sprintf("[SUCCESS] AI %s %s retreat", aiCreatureObject->getNetworkId().getValueString().c_str(), aiCreatureObject->isHateListAutoExpireTargetEnabled() ? "will now" : "will no longer")), Unicode::emptyString);
result += getErrorMessage(argv[0], ERR_SUCCESS);
}
}
}
else if (isAbbrev(argv[0], "walkspeed"))
{
CreatureObject * const userCreatureObject = CreatureObject::getCreatureObject(userId);
if (userCreatureObject == nullptr)
{
result += getErrorMessage(argv[0], ERR_FAIL);
}
else
{
NetworkId aiNetworkId;
CreatureObject * aiCreatureObject = nullptr;
if (argv.size() > 2)
{
// Get the network id and speed
aiNetworkId = NetworkId(Unicode::wideToNarrow(argv[1]));
aiCreatureObject = CreatureObject::getCreatureObject(aiNetworkId);
if ( (aiCreatureObject != nullptr)
&& !aiCreatureObject->isPlayerControlled())
{
aiCreatureObject->setBaseWalkSpeed(Unicode::toFloat(argv[2]));
}
}
else if (argv.size() > 1)
{
// Get the look at target and speed
aiNetworkId = userCreatureObject->getLookAtTarget();
aiCreatureObject = CreatureObject::getCreatureObject(aiNetworkId);
if ( (aiCreatureObject != nullptr)
&& !aiCreatureObject->isPlayerControlled())
{
aiCreatureObject->setBaseWalkSpeed(Unicode::toFloat(argv[1]));
}
}
if ( (aiCreatureObject == nullptr)
|| aiCreatureObject->isPlayerControlled())
{
Chat::sendSystemMessage(*userCreatureObject, Unicode::narrowToWide(fs.sprintf("[FAILURE] Unable to resolve %s to an AI", aiNetworkId.getValueString().c_str())), Unicode::emptyString);
result += getErrorMessage(argv[0], ERR_FAIL);
}
else
{
Chat::sendSystemMessage(*userCreatureObject, Unicode::narrowToWide(fs.sprintf("[SUCCESS] AI %s has a walk speed of %.1fm/s", aiCreatureObject->getNetworkId().getValueString().c_str(), aiCreatureObject->getBaseWalkSpeed())), Unicode::emptyString);
result += getErrorMessage(argv[0], ERR_SUCCESS);
}
}
}
else if (isAbbrev(argv[0], "runspeed"))
{
CreatureObject * const userCreatureObject = CreatureObject::getCreatureObject(userId);
if (userCreatureObject == nullptr)
{
result += getErrorMessage(argv[0], ERR_FAIL);
}
else
{
NetworkId aiNetworkId;
CreatureObject * aiCreatureObject = nullptr;
if (argv.size() > 2)
{
// Get the network id and speed
aiNetworkId = NetworkId(Unicode::wideToNarrow(argv[1]));
aiCreatureObject = CreatureObject::getCreatureObject(aiNetworkId);
if ( (aiCreatureObject != nullptr)
&& !aiCreatureObject->isPlayerControlled())
{
aiCreatureObject->setBaseRunSpeed(Unicode::toFloat(argv[2]));
}
}
else if (argv.size() > 1)
{
// Get the look at target and speed
aiNetworkId = userCreatureObject->getLookAtTarget();
aiCreatureObject = CreatureObject::getCreatureObject(aiNetworkId);
if ( (aiCreatureObject != nullptr)
&& !aiCreatureObject->isPlayerControlled())
{
aiCreatureObject->setBaseRunSpeed(Unicode::toFloat(argv[1]));
}
}
if ( (aiCreatureObject == nullptr)
|| aiCreatureObject->isPlayerControlled())
{
Chat::sendSystemMessage(*userCreatureObject, Unicode::narrowToWide(fs.sprintf("[FAILURE] Unable to resolve %s to an AI", aiNetworkId.getValueString().c_str())), Unicode::emptyString);
result += getErrorMessage(argv[0], ERR_FAIL);
}
else
{
Chat::sendSystemMessage(*userCreatureObject, Unicode::narrowToWide(fs.sprintf("[SUCCESS] AI %s has a run speed of %.1fm/s", aiCreatureObject->getNetworkId().getValueString().c_str(), aiCreatureObject->getBaseRunSpeed())), Unicode::emptyString);
result += getErrorMessage(argv[0], ERR_SUCCESS);
}
}
}
#ifdef _DEBUG
else if (isAbbrev(argv[0], "killall"))
{
CreatureObject * const userCreatureObject = CreatureObject::getCreatureObject(userId);
if (userCreatureObject != nullptr)
{
NetworkId sparedAiNetworkId;
// See if we need to spare any single AI
{
if (argv.size() > 1)
{
sparedAiNetworkId = NetworkId(Unicode::wideToNarrow(argv[1]));
}
else
{
sparedAiNetworkId = userCreatureObject->getLookAtTarget();
}
}
// Run through all the AI on the server and kill them
int const serverObjectCount = ServerWorld::getNumObjects();
int killCount = 0;
for (int i = 0; i < serverObjectCount; ++i)
{
ServerObject * const serverObject = ServerWorld::getObject(i);
CreatureObject * const creatureObject = (serverObject != nullptr) ? serverObject->asCreatureObject() : nullptr;
if ( (creatureObject != nullptr)
&& !creatureObject->isDead()
&& !creatureObject->isPlayerControlled()
&& (creatureObject->getNetworkId() != sparedAiNetworkId))
{
creatureObject->setIncapacitated(true, NetworkId::cms_invalid);
++killCount;
}
}
Chat::sendSystemMessage(*userCreatureObject, Unicode::narrowToWide(fs.sprintf("[SUCCESS] %d AI terminated.", killCount)), Unicode::emptyString);
result += getErrorMessage(argv[0], ERR_SUCCESS);
}
else
{
result += getErrorMessage(argv[0], ERR_FAIL);
}
}
else if (isAbbrev(argv[0], "debug"))
{
Object * const object = NetworkIdManager::getObjectById(userId);
ServerObject * const serverObject = (object != nullptr) ? object->asServerObject() : nullptr;
int temp;
if (serverObject != nullptr)
{
if ( serverObject->getObjVars().getItem("ai_debug_string", temp)
&& (temp > 0))
{
if (!AiShipController::isClientDebugEnabled())
{
AiShipController::setClientDebugEnabled(true);
Chat::sendSystemMessage(*serverObject, Unicode::narrowToWide("[ENABLED] Your client is requesting ai debugging."), Unicode::emptyString);
}
else
{
IGNORE_RETURN(serverObject->setObjVarItem("ai_debug_string", 0));
Chat::sendSystemMessage(*serverObject, Unicode::narrowToWide("[DISABLED] Your client is NO LONGER requesting ai debugging."), Unicode::emptyString);
// Tell the client to remove all ai debugging
Client * const client = serverObject->getClient();
if (client != nullptr)
{
AiDebugString aiDebugString;
aiDebugString.enableClearClientFlag();
bool const reliable = true;
GenericValueTypeMessage<std::pair<NetworkId, std::string> > message("AiDebugString", std::make_pair(serverObject->getNetworkId(), aiDebugString.toString()));
client->send(message, reliable);
}
}
}
else
{
IGNORE_RETURN(serverObject->setObjVarItem("ai_debug_string", 1));
Chat::sendSystemMessage(*serverObject, Unicode::narrowToWide("[ENABLED] Your client is requesting ai debugging."), Unicode::emptyString);
if (!AiShipController::isClientDebugEnabled())
{
AiShipController::setClientDebugEnabled(true);
}
}
}
result += getErrorMessage(argv[0], ERR_SUCCESS);
}
else if (isAbbrev(argv[0], "debugText"))
{
Object * const object = NetworkIdManager::getObjectById(userId);
ServerObject * const serverObject = (object != nullptr) ? object->asServerObject() : nullptr;
if (serverObject != nullptr)
{
AiDebugString::setTextEnabled(!AiDebugString::isTextEnabled());
Unicode::String systemMessage;
systemMessage = Unicode::narrowToWide(AiDebugString::isTextEnabled() ? "[ENABLED] Client-side debug text is enabled." : "[DISABLED] Client-side debug text is disabled.");
Chat::sendSystemMessage(*serverObject, systemMessage, Unicode::emptyString);
}
result += getErrorMessage(argv[0], ERR_SUCCESS);
}
#endif //_DEBUG
else
{
result += getErrorMessage(argv[0], ERR_NO_HANDLER);
}
return true;
}
// ======================================================================