Reece
c1976d771b
[*] Disambiguate auContainerUtils [*] Optimize AuLogXXX / remove alloc [*] Didn't commit two auROXTL idiom updates (ExceptionWatcher and Logging)
199 lines
6.3 KiB
C++
199 lines
6.3 KiB
C++
/***
|
|
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
|
|
|
File: Commands.cpp
|
|
Date: 2021-6-12
|
|
Author: Reece
|
|
***/
|
|
#include <Source/RuntimeInternal.hpp>
|
|
#include "Commands.hpp"
|
|
|
|
namespace Aurora::Console::Commands
|
|
{
|
|
struct Command;
|
|
struct CommandDispatch;
|
|
|
|
static AuHashMap<AuString, Command> gCommands;
|
|
static AuList<Hooks::LineHook_cb> gLineCallbacks;
|
|
static AuList<CommandDispatch> gPendingCommands;
|
|
static auto gMutex = AuThreadPrimitives::MutexUnique();
|
|
static auto gPendingCommandsMutex = AuThreadPrimitives::MutexUnique();
|
|
static Async::WorkerPId_t gCommandDispatcher;
|
|
|
|
struct Command
|
|
{
|
|
AuString tag;
|
|
Parse::ParseObject commandStructure;
|
|
AuSPtr<ICommandSubscriber> callback;
|
|
|
|
Command(AuString tag, Parse::ParseObject commandStructure, const AuSPtr<ICommandSubscriber> &callback) : tag(tag), commandStructure(commandStructure), callback(callback) {}
|
|
Command(AuString tag, Parse::ParseObject commandStructure, AuSPtr<ICommandSubscriber> &&callback) : tag(tag), commandStructure(commandStructure), callback(AuMove(callback)) {}
|
|
};
|
|
|
|
struct CommandDispatch
|
|
{
|
|
Parse::ParsedObject arguments;
|
|
AuSPtr<ICommandSubscriber> callback;
|
|
|
|
CommandDispatch(const Parse::ParsedObject &arguments, const AuSPtr<ICommandSubscriber> &callback) : arguments(arguments), callback(callback) {}
|
|
};
|
|
|
|
enum class EDispatchType
|
|
{
|
|
eNow,
|
|
eSys,
|
|
eAsync
|
|
};
|
|
|
|
static bool Dispatch(const AuString &string, EDispatchType type, Async::WorkerPId_t workerId)
|
|
{
|
|
Parse::ParseResult res;
|
|
AuSPtr<ICommandSubscriber> callback;
|
|
|
|
{
|
|
AU_LOCK_GUARD(gPendingCommandsMutex);
|
|
|
|
{
|
|
AuString tag;
|
|
AuString cmdParse;
|
|
AuMach offset;
|
|
|
|
auto commandSplit = string.find(" ");
|
|
if (commandSplit != AuString::npos)
|
|
{
|
|
tag = string.substr(0, commandSplit);
|
|
cmdParse = string.substr(commandSplit + 1);
|
|
}
|
|
else
|
|
{
|
|
tag = string;
|
|
}
|
|
|
|
auto cmdItr = gCommands.find(tag);
|
|
if (cmdItr == gCommands.end())
|
|
{
|
|
AuLogWarn("Command {} does not exist", tag);
|
|
return false;
|
|
}
|
|
|
|
auto const &cmdEntry = cmdItr->second;
|
|
|
|
offset = 0;
|
|
Parse::ParseState consumable(AuIO::Character::ProviderFromStringShared(cmdParse, offset));
|
|
auto status = Parse::Parse(consumable, cmdEntry.commandStructure, res);
|
|
|
|
if (!status)
|
|
{
|
|
AuLogWarn("Couldn't parse command {}", string);
|
|
return false;
|
|
}
|
|
|
|
if (type == EDispatchType::eSys)
|
|
{
|
|
gPendingCommands.push_back(CommandDispatch(res.result, cmdEntry.callback));
|
|
}
|
|
else
|
|
{
|
|
callback = cmdEntry.callback;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (type == EDispatchType::eNow)
|
|
{
|
|
callback->OnCommand(res.result);
|
|
}
|
|
else
|
|
{
|
|
Async::DispatchWork<CommandDispatch>(workerId,
|
|
Async::TaskFromConsumerRefT<CommandDispatch>([](const CommandDispatch &dispatch) -> void
|
|
{
|
|
dispatch.callback->OnCommand(dispatch.arguments);
|
|
}),
|
|
{},
|
|
CommandDispatch(res.result, callback),
|
|
false
|
|
);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
AUKN_SYM void AddCommand(const AuString &tag, const Parse::ParseObject &commandStructure, const AuSPtr<ICommandSubscriber> &callback)
|
|
{
|
|
AU_LOCK_GUARD(gPendingCommandsMutex);
|
|
gCommands.insert(AuMakePair(tag, Command(tag, commandStructure, callback)));
|
|
}
|
|
|
|
AUKN_SYM bool DispatchCommand(const AuString &string)
|
|
{
|
|
return Dispatch(string, EDispatchType::eSys, {});
|
|
}
|
|
|
|
AUKN_SYM bool DispatchCommandThisThread(const AuString &string)
|
|
{
|
|
return Dispatch(string, EDispatchType::eNow, {});
|
|
}
|
|
|
|
AUKN_SYM bool DispatchCommandToAsyncRunner(const AuString &string, Async::WorkerId_t id)
|
|
{
|
|
return Dispatch(string, EDispatchType::eAsync, AuAsync::WorkerPId_t(AuUnsafeRaiiToShared(AuAsync::GetAsyncApp()), id));
|
|
}
|
|
|
|
AUKN_SYM bool DispatchCommandToAsyncRunner(const AuString &string, Async::WorkerPId_t id)
|
|
{
|
|
return Dispatch(string, EDispatchType::eAsync, id);
|
|
}
|
|
|
|
void UpdateDispatcher(AuOptional<Async::WorkerId_t> target)
|
|
{
|
|
AU_LOCK_GUARD(gMutex);
|
|
AU_LOCK_GUARD(gPendingCommandsMutex);
|
|
|
|
// process commands before async app termination
|
|
if ((!target.has_value()))
|
|
{
|
|
auto commands = AuExchange(gPendingCommands, {});
|
|
for (const auto &command : commands)
|
|
{
|
|
command.callback->OnCommand(command.arguments);
|
|
}
|
|
}
|
|
|
|
gCommandDispatcher = Async::WorkerPId_t(AuUnsafeRaiiToShared(AuAsync::GetAsyncApp()), target.value_or(AuAsync::ThreadGroup_t {}));
|
|
}
|
|
|
|
static void DispatchCommandsFromThis(const AuList<CommandDispatch> &commands)
|
|
{
|
|
for (const auto &command : commands)
|
|
{
|
|
command.callback->OnCommand(command.arguments);
|
|
}
|
|
}
|
|
|
|
void PumpCommands()
|
|
{
|
|
AU_LOCK_GUARD(gMutex);
|
|
AuList<CommandDispatch> commands;
|
|
|
|
{
|
|
AU_LOCK_GUARD(gPendingCommandsMutex);
|
|
commands = AuExchange(gPendingCommands, {});
|
|
}
|
|
|
|
if ((gCommandDispatcher.pool == nullptr) ||
|
|
((gCommandDispatcher.pool.get() == Async::GetAsyncApp()) &&
|
|
(gCommandDispatcher == Async::WorkerId_t{})))
|
|
{
|
|
DispatchCommandsFromThis(commands);
|
|
}
|
|
else
|
|
{
|
|
NewWorkItem(gCommandDispatcher,
|
|
AuMakeShared<Async::BasicWorkStdFunc>([&commands]()
|
|
{
|
|
DispatchCommandsFromThis(commands);
|
|
}), true)->Dispatch()->BlockUntilComplete();
|
|
}
|
|
}
|
|
} |