DarkflameServer/thirdparty/raknet/Source/ConsoleServer.cpp
2021-12-05 18:54:36 +01:00

278 lines
9.2 KiB
C++

#include "ConsoleServer.h"
#include "TransportInterface.h"
#include "CommandParserInterface.h"
#include <string.h>
#include <stdlib.h>
#define COMMAND_DELINATOR ' '
#define COMMAND_DELINATOR_TOGGLE '"'
#include "LinuxStrings.h"
ConsoleServer::ConsoleServer()
{
transport=0;
password[0]=0;
}
ConsoleServer::~ConsoleServer()
{
}
void ConsoleServer::SetTransportProvider(TransportInterface *transportInterface, unsigned short port)
{
// Replace the current TransportInterface, stopping the old one, if present, and starting the new one.
if (transportInterface)
{
if (transport)
{
RemoveCommandParser(transport->GetCommandParser());
transport->Stop();
}
transport=transportInterface;
transport->Start(port, true);
unsigned i;
for (i=0; i < commandParserList.Size(); i++)
commandParserList[i]->OnTransportChange(transport);
// The transport itself might have a command parser - for example password for the RakNet transport
AddCommandParser(transport->GetCommandParser());
}
}
void ConsoleServer::AddCommandParser(CommandParserInterface *commandParserInterface)
{
if (commandParserInterface==0)
return;
// Non-duplicate insertion
unsigned i;
for (i=0; i < commandParserList.Size(); i++)
{
if (commandParserList[i]==commandParserInterface)
return;
if (_stricmp(commandParserList[i]->GetName(), commandParserInterface->GetName())==0)
{
// Naming conflict between two command parsers
assert(0);
return;
}
}
commandParserList.Insert(commandParserInterface);
if (transport)
commandParserInterface->OnTransportChange(transport);
}
void ConsoleServer::RemoveCommandParser(CommandParserInterface *commandParserInterface)
{
if (commandParserInterface==0)
return;
// Overwrite the element we are removing from the back of the list and delete the back of the list
unsigned i;
for (i=0; i < commandParserList.Size(); i++)
{
if (commandParserList[i]==commandParserInterface)
{
commandParserList[i]=commandParserList[commandParserList.Size()-1];
commandParserList.RemoveFromEnd();
return;
}
}
}
void ConsoleServer::Update(void)
{
unsigned i;
char *parameterList[20]; // Up to 20 parameters
unsigned numParameters;
SystemAddress newOrLostConnectionId;
Packet *p;
RegisteredCommand rc;
p = transport->Receive();
newOrLostConnectionId=transport->HasNewConnection();
if (newOrLostConnectionId!=UNASSIGNED_SYSTEM_ADDRESS)
{
for (i=0; i < commandParserList.Size(); i++)
{
commandParserList[i]->OnNewIncomingConnection(newOrLostConnectionId, transport);
}
transport->Send(newOrLostConnectionId, "Connected to remote command console.\r\nType 'help' for help.\r\n");
ListParsers(newOrLostConnectionId);
}
newOrLostConnectionId=transport->HasLostConnection();
if (newOrLostConnectionId!=UNASSIGNED_SYSTEM_ADDRESS)
{
for (i=0; i < commandParserList.Size(); i++)
commandParserList[i]->OnConnectionLost(newOrLostConnectionId, transport);
}
while (p)
{
bool commandParsed=false;
char copy[REMOTE_MAX_TEXT_INPUT];
memcpy(copy, p->data, p->length);
copy[p->length]=0;
CommandParserInterface::ParseConsoleString((char*)p->data, COMMAND_DELINATOR, COMMAND_DELINATOR_TOGGLE, &numParameters, parameterList, 20); // Up to 20 parameters
if (numParameters==0)
{
transport->DeallocatePacket(p);
p = transport->Receive();
continue;
}
if (_stricmp(*parameterList, "help")==0 && numParameters<=2)
{
// Find the parser specified and display help for it
if (numParameters==1)
{
transport->Send(p->systemAddress, "\r\nINSTRUCTIONS:\r\n");
transport->Send(p->systemAddress, "Enter commands on your keyboard, using spaces to delineate parameters.\r\n");
transport->Send(p->systemAddress, "You can use quotation marks to toggle space delineation.\r\n");
transport->Send(p->systemAddress, "You can connect multiple times from the same computer.\r\n");
transport->Send(p->systemAddress, "You can direct commands to a parser by prefixing the parser name or number.\r\n");
transport->Send(p->systemAddress, "COMMANDS:\r\n");
transport->Send(p->systemAddress, "help Show this display.\r\n");
transport->Send(p->systemAddress, "help <ParserName> Show help on a particular parser.\r\n");
transport->Send(p->systemAddress, "help <CommandName> Show help on a particular command.\r\n");
transport->Send(p->systemAddress, "quit Disconnects from the server.\r\n");
transport->Send(p->systemAddress, "[<ParserName>] <Command> [<Parameters>] Execute a command\r\n");
transport->Send(p->systemAddress, "[<ParserNumber>] <Command> [<Parameters>] Execute a command\r\n");
ListParsers(p->systemAddress);
}
else // numParameters == 2, including the help tag
{
for (i=0; i < commandParserList.Size(); i++)
{
if (_stricmp(parameterList[1], commandParserList[i]->GetName())==0)
{
commandParsed=true;
commandParserList[i]->SendHelp(transport, p->systemAddress);
transport->Send(p->systemAddress, "COMMAND LIST:\r\n");
commandParserList[i]->SendCommandList(transport, p->systemAddress);
transport->Send(p->systemAddress, "\r\n");
break;
}
}
if (commandParsed==false)
{
// Try again, for all commands for all parsers.
RegisteredCommand rc;
for (i=0; i < commandParserList.Size(); i++)
{
if (commandParserList[i]->GetRegisteredCommand(parameterList[1], &rc))
{
if (rc.parameterCount==CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS)
transport->Send(p->systemAddress, "(Variable parms): %s %s\r\n", rc.command, rc.commandHelp);
else
transport->Send(p->systemAddress, "(%i parms): %s %s\r\n", rc.parameterCount, rc.command, rc.commandHelp);
commandParsed=true;
break;
}
}
}
if (commandParsed==false)
{
// Don't know what to do
transport->Send(p->systemAddress, "Unknown help topic: %s.\r\n", parameterList[1]);
}
}
}
else if (_stricmp(*parameterList, "quit")==0 && numParameters==1)
{
transport->Send(p->systemAddress, "Goodbye!");
transport->CloseConnection(p->systemAddress);
}
else
{
bool tryAllParsers=true;
bool failed=false;
if (numParameters >=2) // At minimum <CommandParserName> <Command>
{
unsigned commandParserIndex=(unsigned)-1;
// Prefixing with numbers directs to a particular parser
if (**parameterList>='0' && **parameterList<='9')
{
commandParserIndex=atoi(*parameterList); // Use specified parser unless it's an invalid number
commandParserIndex--; // Subtract 1 since we displayed numbers starting at index+1
if (commandParserIndex >= commandParserList.Size())
{
transport->Send(p->systemAddress, "Invalid index.\r\n");
failed=true;
}
}
else
{
// // Prefixing with the name of a command parser directs to that parser. See if the first word matches a parser
for (i=0; i < commandParserList.Size(); i++)
{
if (_stricmp(parameterList[0], commandParserList[i]->GetName())==0)
{
commandParserIndex=i; // Matches parser at index i
break;
}
}
}
if (failed==false)
{
// -1 means undirected, so otherwise this is directed to a target
if (commandParserIndex!=(unsigned)-1)
{
// Only this parser should use this command
tryAllParsers=false;
if (commandParserList[commandParserIndex]->GetRegisteredCommand(parameterList[1], &rc))
{
commandParsed=true;
if (rc.parameterCount==CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS || rc.parameterCount==numParameters-2)
commandParserList[commandParserIndex]->OnCommand(rc.command, numParameters-2, parameterList+2, transport, p->systemAddress, copy);
else
transport->Send(p->systemAddress, "Invalid parameter count.\r\n(%i parms): %s %s\r\n", rc.parameterCount, rc.command, rc.commandHelp);
}
}
}
}
if (failed == false && tryAllParsers)
{
for (i=0; i < commandParserList.Size(); i++)
{
// Undirected command. Try all the parsers to see if they understand the command
// Pass the 1nd element as the command, and the remainder as the parameter list
if (commandParserList[i]->GetRegisteredCommand(parameterList[0], &rc))
{
commandParsed=true;
if (rc.parameterCount==CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS || rc.parameterCount==numParameters-1)
commandParserList[i]->OnCommand(rc.command, numParameters-1, parameterList+1, transport, p->systemAddress, copy);
else
transport->Send(p->systemAddress, "Invalid parameter count.\r\n(%i parms): %s %s\r\n", rc.parameterCount, rc.command, rc.commandHelp);
}
}
}
if (commandParsed==false && commandParserList.Size() > 0)
{
transport->Send(p->systemAddress, "Unknown command: Type 'help' for help.\r\n");
}
}
transport->DeallocatePacket(p);
p = transport->Receive();
}
}
void ConsoleServer::ListParsers(SystemAddress systemAddress)
{
transport->Send(systemAddress,"INSTALLED PARSERS:\r\n");
unsigned i;
for (i=0; i < commandParserList.Size(); i++)
{
transport->Send(systemAddress, "%i. %s\r\n", i+1, commandParserList[i]->GetName());
}
}