uox3/source/commands.cpp
2022-06-16 08:17:28 -04:00

599 lines
21 KiB
C++

#include "uox3.h"
#include "commands.h"
#include "cServerDefinitions.h"
#include "ssection.h"
#include "CJSMapping.h"
#include "cScript.h"
#include "CPacketSend.h"
#include "StringUtility.hpp"
cCommands *Commands = nullptr;
auto cCommands::startup() ->void {
CommandReset() ;
}
cCommands::~cCommands()
{
CommandMap.clear();
TargetMap.clear();
JSCommandMap.clear();
for( size_t clearIter = 0; clearIter < clearance.size(); ++clearIter )
{
delete clearance[clearIter];
clearance[clearIter] = nullptr;
}
clearance.clear();
}
//o-----------------------------------------------------------------------------------------------o
//| Function - UI08 NumArguments( void )
//| Date - 3/12/2003
//| Changes - 4/2/2003 - Reduced to a UI08
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Number of arguments in a command
//o-----------------------------------------------------------------------------------------------o
UI08 cCommands::NumArguments( void )
{
auto secs = oldstrutil::sections( commandString, " ");
return static_cast<UI08>(secs.size());
}
//o-----------------------------------------------------------------------------------------------o
//| Function - SI32 Argument( UI08 argNum )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Grabs argument argNum and converts it to an integer
//o-----------------------------------------------------------------------------------------------o
SI32 cCommands::Argument( UI08 argNum )
{
SI32 retVal = 0;
std::string tempString = CommandString( argNum + 1, argNum + 1 );
if( !tempString.empty() )
{
try {
retVal = std::stoi(tempString, nullptr, 0);
}
catch (const std::invalid_argument & e) {
Console.error( oldstrutil::format( "[%s] Unable to convert command argument ('%s') to integer.", e.what(), tempString.c_str() ));
}
}
return retVal;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - std::string CommandString( UI08 section, UI08 end )
//| Date - 4/02/2003
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Gets/Sets the Comm value
//o-----------------------------------------------------------------------------------------------o
std::string cCommands::CommandString( UI08 section, UI08 end )
{
std::string retString;
if( end != 0 )
{
retString = oldstrutil::extractSection(commandString, " ", section - 1, end - 1 );
}
else
{
retString = oldstrutil::extractSection(commandString, " ", section - 1 );
}
return retString;
}
void cCommands::CommandString( std::string newValue )
{
commandString = newValue;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void Command( CSocket *s, CChar *mChar, std::string text, bool checkSocketAccess )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Handles commands sent from client
//o-----------------------------------------------------------------------------------------------o
//| Changes - 25 June, 2003
//| Made it accept a CPITalkRequest, allowing to remove
//| the need for Offset and unicode decoding
//o-----------------------------------------------------------------------------------------------o
void cCommands::Command( CSocket *s, CChar *mChar, std::string text, bool checkSocketAccess )
{
CommandString( oldstrutil::simplify( text ));
if( NumArguments() < 1 )
{
return;
}
// Discard the leading command prefix
std::string command = oldstrutil::upper( CommandString( 1, 1 ));
JSCOMMANDMAP_ITERATOR toFind = JSCommandMap.find( command );
if( toFind != JSCommandMap.end() )
{
if( toFind->second.isEnabled )
{
bool plClearance = false;
if( checkSocketAccess )
plClearance = ( s->CurrcharObj()->GetCommandLevel() >= toFind->second.cmdLevelReq || s->CurrcharObj()->GetAccount().wAccountIndex == 0 );
else
plClearance = ( mChar->GetCommandLevel() >= toFind->second.cmdLevelReq || mChar->GetAccount().wAccountIndex == 0 );
// from now on, account 0 ALWAYS has admin access, regardless of command level
if( !plClearance )
{
if( checkSocketAccess )
Log( command, s->CurrcharObj(), nullptr, "Insufficient clearance" );
else
Log( command, mChar, nullptr, "Insufficient clearance" );
s->sysmessage( 337 );
return;
}
cScript *toExecute = JSMapping->GetScript( toFind->second.scriptID );
if( toExecute != nullptr )
{ // All commands that execute are of the form: command_commandname (to avoid possible clashes)
#if defined( UOX_DEBUG_MODE )
Console.print( oldstrutil::format("Executing JS command %s\n", command.c_str() ));
#endif
toExecute->executeCommand( s, "command_" + command, CommandString( 2 ) );
}
if( checkSocketAccess )
Log( command, s->CurrcharObj(), nullptr, "Cleared" );
else
Log( command, mChar, nullptr, "Cleared" );
return;
}
}
TARGETMAP_ITERATOR findTarg = TargetMap.find( command );
if( findTarg != TargetMap.end() )
{
bool plClearance = false;
if( checkSocketAccess )
plClearance = ( s->CurrcharObj()->GetCommandLevel() >= findTarg->second.cmdLevelReq || s->CurrcharObj()->GetAccount().wAccountIndex == 0 );
else
plClearance = ( mChar->GetCommandLevel() >= findTarg->second.cmdLevelReq || mChar->GetAccount().wAccountIndex == 0 );
if( !plClearance )
{
if( checkSocketAccess )
Log( command, s->CurrcharObj(), nullptr, "Insufficient clearance" );
else
Log( command, mChar, nullptr, "Insufficient clearance" );
s->sysmessage( 337 );
return;
}
if( checkSocketAccess )
Log( command, s->CurrcharObj(), nullptr, "Cleared" );
else
Log( command, mChar, nullptr, "Cleared" );
switch( findTarg->second.cmdType )
{
case CMD_TARGET:
s->target( 0, findTarg->second.targID, 0, findTarg->second.dictEntry );
break;
case CMD_TARGETXYZ:
if( NumArguments() == 4 )
{
s->ClickX( static_cast<SI16>(Argument( 1 )) );
s->ClickY( static_cast<SI16>(Argument( 2 )) );
s->ClickZ( static_cast<SI08>(Argument( 3 )) );
s->target( 0, findTarg->second.targID, 0, findTarg->second.dictEntry );
}
else
s->sysmessage( 340 );
break;
case CMD_TARGETINT:
if( NumArguments() == 2 )
{
s->TempInt( Argument( 1 ) );
s->target( 0, findTarg->second.targID, 0, findTarg->second.dictEntry );
}
else
s->sysmessage( 338 );
break;
case CMD_TARGETTXT:
if( NumArguments() > 1 )
{
s->XText( CommandString( 2 ) );
s->target( 0, findTarg->second.targID, 0, findTarg->second.dictEntry );
}
else
s->sysmessage( 9026 ); // This command requires more arguments!
break;
}
}
else
{
COMMANDMAP_ITERATOR toFind = CommandMap.find( command );
if( toFind == CommandMap.end() )
{
bool cmdHandled = false;
std::vector<UI16> scriptTriggers = mChar->GetScriptTriggers();
for( auto scriptTrig : scriptTriggers )
{
cScript *toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
// -1 == event doesn't exist, or returned -1
// 0 == script returned false, 0, or nothing
// 1 == script returned true or 1, don't execute hard code
if( toExecute->OnCommand( s, command ) == 1 )
{
cmdHandled = true;
}
}
}
if( !cmdHandled )
{
s->sysmessage( 336 ); // Unrecognized command.
}
return;
}
else
{
bool plClearance = false;
if( checkSocketAccess )
plClearance = ( s->CurrcharObj()->GetCommandLevel() >= toFind->second.cmdLevelReq || s->CurrcharObj()->GetAccount().wAccountIndex == 0 );
else
plClearance = ( mChar->GetCommandLevel() >= toFind->second.cmdLevelReq || mChar->GetAccount().wAccountIndex == 0 );
// from now on, account 0 ALWAYS has admin access, regardless of command level
if( !plClearance )
{
if( checkSocketAccess )
Log( command, s->CurrcharObj(), nullptr, "Insufficient clearance" );
else
Log( command, mChar, nullptr, "Insufficient clearance" );
s->sysmessage( 337 );
return;
}
if( checkSocketAccess )
Log( command, s->CurrcharObj(), nullptr, "Cleared" );
else
Log( command, mChar, nullptr, "Cleared" );
switch( toFind->second.cmdType )
{
case CMD_FUNC:
(*((CMD_EXEC)toFind->second.cmd_extra)) ();
break;
case CMD_SOCKFUNC:
(*((CMD_SOCKEXEC)toFind->second.cmd_extra)) (s);
break;
default:
s->sysmessage( 346 );
break;
}
}
}
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void Load( void )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Load the command table
//o-----------------------------------------------------------------------------------------------o
void cCommands::Load( void )
{
SI16 commandCount = 0;
ScriptSection *commands = FileLookup->FindEntry( "COMMAND_OVERRIDE", command_def );
if( commands == nullptr ) {
InitClearance();
return;
}
std::string tag;
std::string data;
std::string UTag;
std::vector< std::string > badCommands;
for( tag = commands->First(); !commands->AtEnd(); tag = commands->Next() )
{
data = commands->GrabData();
COMMANDMAP_ITERATOR toFind = CommandMap.find( tag );
TARGETMAP_ITERATOR findTarg = TargetMap.find( tag );
if( toFind == CommandMap.end() && findTarg == TargetMap.end() )
badCommands.push_back( tag ); // make sure we don't index into array at -1
else
{
++commandCount;
if( toFind != CommandMap.end() )
{
toFind->second.cmdLevelReq = static_cast<UI08>(std::stoul(data, nullptr, 0)) ;
}
else if( findTarg != TargetMap.end() )
{
findTarg->second.cmdLevelReq = static_cast<UI08>(std::stoul(data, nullptr, 0));
}
}
// check for commands here
}
if( !badCommands.empty() )
{
Console << myendl;
std::for_each(badCommands.begin(), badCommands.end(), [this](const std::string &entry){
Console << "Invalid command '" << entry.c_str() << "' found in commands.dfn!" << myendl;
});
}
Console << " o Loading command levels";
#if defined( UOX_DEBUG_MODE )
Console << myendl;
#endif
ScriptSection *cmdClearance = FileLookup->FindEntry( "COMMANDLEVELS", command_def );
if( cmdClearance == nullptr )
InitClearance();
else
{
size_t currentWorking;
for (const auto &sec : cmdClearance->collection()){
tag = sec->tag ;
data = sec->data ;
currentWorking = clearance.size();
clearance.push_back( new commandLevel_st );
clearance[currentWorking]->name = tag;
clearance[currentWorking]->commandLevel = static_cast<UI08>(std::stoul(data, nullptr, 0));
}
std::vector< commandLevel_st * >::const_iterator cIter;
for( cIter = clearance.begin(); cIter != clearance.end(); ++cIter ) {
commandLevel_st *ourClear = (*cIter);
if(ourClear){
cmdClearance = FileLookup->FindEntry( ourClear->name, command_def );
if( cmdClearance){
for (const auto &sec : cmdClearance->collection()){
tag = sec->tag;
data = sec->data ;
UTag = oldstrutil::upper( tag );
if( UTag == "NICKCOLOUR" ) {
ourClear->nickColour = static_cast<UI16>(std::stoul(data, nullptr, 0));
}
else if( UTag == "TITLE" ) {
ourClear->title = data;
}
else if( UTag == "DEFAULTPRIV" ) {
ourClear->defaultPriv = static_cast<UI16>(std::stoul(data, nullptr, 0));
}
else if( UTag == "BODYID" ) {
ourClear->targBody = static_cast<UI16>(std::stoul(data, nullptr, 0));
}
else if( UTag == "ALLSKILL" ) {
ourClear->allSkillVals = static_cast<UI16>(std::stoul(data, nullptr, 0));
}
else if( UTag == "BODYCOLOUR" ) {
ourClear->bodyColour = static_cast<UI16>(std::stoul(data, nullptr, 0));
}
else if( UTag == "STRIPHAIR" ) {
ourClear->stripOff.set( BIT_STRIPHAIR, true );
}
else if( UTag == "STRIPITEMS" ) {
ourClear->stripOff.set( BIT_STRIPITEMS, true );
}
else {
Console << myendl << "Unknown tag in " << ourClear->name << ": " << tag << " with data of " << data << myendl;
}
}
}
}
}
}
// Now we'll load our JS commands, what fun!
CJSMappingSection *commandSection = JSMapping->GetSection( SCPT_COMMAND );
for( cScript *ourScript = commandSection->First(); !commandSection->Finished(); ourScript = commandSection->Next() )
{
if( ourScript != nullptr )
ourScript->ScriptRegistration( "Command" );
}
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void Log( const std::string &command, CChar *player1, CChar *player2, const std::string &extraInfo )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Writes toLog to a file
//o-----------------------------------------------------------------------------------------------o
void cCommands::Log( const std::string &command, CChar *player1, CChar *player2, const std::string &extraInfo )
{
std::string logName = cwmWorldState->ServerData()->Directory( CSDDP_LOGS ) + "command.log";
std::ofstream logDestination;
logDestination.open( logName.c_str(), std::ios::out | std::ios::app );
if( !logDestination.is_open() )
{
Console.error( oldstrutil::format("Unable to open command log file %s!", logName.c_str() ));
return;
}
char dateTime[1024];
RealTime( dateTime );
logDestination << "[" << dateTime << "] ";
logDestination << player1->GetName() << " (serial: " << std::hex << player1->GetSerial() << ") ";
logDestination << "used command <" << command << (CommandString( 2 ) != "" ? " " : "") << CommandString( 2 ) << "> ";
if( ValidateObject( player2 ) )
logDestination << "on player " << player2->GetName() << " (serial: " << player2->GetSerial() << " )";
logDestination << "Extra Info: " << extraInfo << std::endl;
logDestination.close();
}
//o-----------------------------------------------------------------------------------------------o
//| Function - commandLevel_st *GetClearance( std::string clearName )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Get the command level of a character
//o-----------------------------------------------------------------------------------------------o
commandLevel_st *cCommands::GetClearance( std::string clearName )
{
if( clearance.empty() )
{
return nullptr;
}
commandLevel_st *clearPointer;
std::vector< commandLevel_st * >::const_iterator clearIter;
for( clearIter = clearance.begin(); clearIter != clearance.end(); ++clearIter )
{
clearPointer = (*clearIter);
if( oldstrutil::upper( clearName ) == clearPointer->name )
{
return clearPointer;
}
}
return nullptr;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - UI16 GetColourByLevel( UI08 commandLevel )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Get a players nick color based on his command level
//o-----------------------------------------------------------------------------------------------o
UI16 cCommands::GetColourByLevel( UI08 commandLevel )
{
size_t clearanceSize = clearance.size();
if( clearanceSize == 0 )
return 0x005A;
if( commandLevel > clearance[0]->commandLevel )
return clearance[0]->nickColour;
for( size_t counter = 0; counter < ( clearanceSize - 1 ); ++counter )
{
if( commandLevel <= clearance[counter]->commandLevel && commandLevel > clearance[counter+1]->commandLevel )
return clearance[counter]->nickColour;
}
return clearance[clearanceSize - 1]->nickColour;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void InitClearance( void )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Initialize command levels
//o-----------------------------------------------------------------------------------------------o
void cCommands::InitClearance( void )
{
clearance.push_back( new commandLevel_st ); // 0 -> 3
clearance.push_back( new commandLevel_st );
clearance.push_back( new commandLevel_st );
clearance.push_back( new commandLevel_st );
clearance[0]->name = "ADMIN";
clearance[1]->name = "GM";
clearance[2]->name = "COUNSELOR";
clearance[3]->name = "PLAYER";
clearance[0]->title = "Admin";
clearance[1]->title = "GM";
clearance[2]->title = "Counselor";
clearance[3]->title = "";
clearance[0]->commandLevel = 5;
clearance[1]->commandLevel = 2;
clearance[2]->commandLevel = 1;
clearance[3]->commandLevel = 0;
clearance[0]->targBody = 0x03DB;
clearance[1]->targBody = 0x03DB;
clearance[2]->targBody = 0x03DB;
clearance[3]->targBody = 0;
clearance[0]->bodyColour = 0x8001;
clearance[1]->bodyColour = 0x8021;
clearance[2]->bodyColour = 0x8002;
clearance[0]->defaultPriv = 0x786F;
clearance[1]->defaultPriv = 0x786F;
clearance[2]->defaultPriv = 0x0094;
clearance[3]->defaultPriv = 0;
clearance[0]->nickColour = 0x1332;
clearance[1]->nickColour = 0x03;
clearance[2]->nickColour = 0x03;
clearance[3]->nickColour = 0x005A;
clearance[0]->allSkillVals = 1000;
clearance[1]->allSkillVals = 1000;
clearance[2]->allSkillVals = 0;
clearance[3]->allSkillVals = 0;
// Strip Everything for Admins, GMs and Counselors
clearance[0]->stripOff.set( BIT_STRIPHAIR, true );
clearance[0]->stripOff.set( BIT_STRIPITEMS, true );
clearance[1]->stripOff.set( BIT_STRIPHAIR, true );
clearance[1]->stripOff.set( BIT_STRIPITEMS, true );
clearance[2]->stripOff.set( BIT_STRIPHAIR, true );
clearance[2]->stripOff.set( BIT_STRIPITEMS, true );
}
//o-----------------------------------------------------------------------------------------------o
//| Function - commandLevel_st *GetClearance( UI08 commandLevel )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Geta characters command level
//o-----------------------------------------------------------------------------------------------o
commandLevel_st *cCommands::GetClearance( UI08 commandLevel )
{
size_t clearanceSize = clearance.size();
if( clearanceSize == 0 )
return nullptr;
if( commandLevel > clearance[0]->commandLevel )
return clearance[0];
for( size_t counter = 0; counter < ( clearanceSize - 1 ); ++counter )
{
if( commandLevel <= clearance[counter]->commandLevel && commandLevel > clearance[counter+1]->commandLevel )
return clearance[counter];
}
return clearance[clearanceSize - 1];
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool CommandExists( const std::string cmdName )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Check if a command is valid
//o-----------------------------------------------------------------------------------------------o
bool cCommands::CommandExists( const std::string& cmdName )
{
COMMANDMAP_ITERATOR toFind = CommandMap.find( cmdName );
return ( toFind != CommandMap.end() );
}
//o-----------------------------------------------------------------------------------------------o
//| Function - const std::string FirstCommand( void )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Get first command in cmd_table
//o-----------------------------------------------------------------------------------------------o
const std::string cCommands::FirstCommand( void )
{
cmdPointer = CommandMap.begin();
if( FinishedCommandList() )
return "";
return cmdPointer->first;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - const std::string NextCommand( void )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Get next command in cmd_table
//o-----------------------------------------------------------------------------------------------o
const std::string cCommands::NextCommand( void )
{
if( FinishedCommandList() )
return "";
++cmdPointer;
if( FinishedCommandList() )
return "";
return cmdPointer->first;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool FinishedCommandList( void )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Get end of cmd_table
//o-----------------------------------------------------------------------------------------------o
bool cCommands::FinishedCommandList( void )
{
return ( cmdPointer == CommandMap.end() );
}
//o-----------------------------------------------------------------------------------------------o
//| Function - cmdtable_mapentry *CommandDetails( const std::string cmdName )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Get command info
//o-----------------------------------------------------------------------------------------------o
CommandMapEntry *cCommands::CommandDetails( const std::string& cmdName )
{
if( !CommandExists( cmdName ) )
return nullptr;
COMMANDMAP_ITERATOR toFind = CommandMap.find( cmdName );
if( toFind == CommandMap.end() )
return nullptr;
return &(toFind->second);
}