LiteLoaderBDS-1.16.40/ScriptEngine/API/CommandAPI.cpp

539 lines
22 KiB
C++

//#include "DynamicCommandAPI.h"
#include "CommandAPI.h"
#include "McAPI.h"
#include "ItemAPI.h"
#include "PlayerAPI.h"
#include "BlockAPI.h"
#include "CommandOriginAPI.h"
#include "CommandOutputAPI.h"
#include <Tools/Utils.h>
#include <Engine/GlobalShareData.h>
#include <Engine/LocalShareData.h>
#include <Engine/EngineOwnData.h>
#include <Utils/STLHelper.h>
#include <RegCommandAPI.h>
#include <filesystem>
#include <Configs.h>
#include <vector>
#include <string>
#include <MC/ItemInstance.hpp>
#include <MC/ItemStack.hpp>
#include <MC/Dimension.hpp>
#include <ScriptEngine/API/BaseAPI.h>
#include <ScriptEngine/API/EntityAPI.h>
#include <magic_enum/magic_enum.hpp>
#include <third-party/Nlohmann/json.hpp>
//////////////////// Class Definition ////////////////////
ClassDefine<void> PermissionStaticBuilder = EnumDefineBuilder<CommandPermissionLevel>::build("PermType");
ClassDefine<void> ParamTypeStaticBuilder = EnumDefineBuilder<DynamicCommand::ParameterType>::build("ParamType");
ClassDefine<void> ParamOptionStaticBuilder = EnumDefineBuilder<CommandParameterOption>::build("ParamOption");
ClassDefine<CommandClass> CommandClassBuilder =
defineClass<CommandClass>("LLSE_Command")
.constructor(nullptr)
.instanceProperty("name", &CommandClass::getName)
.instanceProperty("registered", &CommandClass::isRegistered)
.instanceFunction("setEnum", &CommandClass::setEnum)
.instanceFunction("setAlias", &CommandClass::setAlias)
//.instanceFunction("newParameter", &CommandClass::newParameter)
.instanceFunction("mandatory", &CommandClass::mandatory)
.instanceFunction("optional", &CommandClass::optional)
.instanceFunction("setSoftEnum", &CommandClass::setSoftEnum)
.instanceFunction("addSoftEnumValues", &CommandClass::addSoftEnumValues)
.instanceFunction("removeSoftEnumValues", &CommandClass::removeSoftEnumValues)
.instanceFunction("getSoftEnumValues", &CommandClass::getSoftEnumValues)
.instanceFunction("getSoftEnumNames", &CommandClass::getSoftEnumNames)
.instanceFunction("overload", &CommandClass::addOverload)
.instanceFunction("setCallback", &CommandClass::setCallback)
.instanceFunction("setup", &CommandClass::setup)
.build();
//////////////////// Helper ////////////////////
bool LLSERemoveCmdCallback(script::ScriptEngine* engine) {
erase_if(localShareData->commandCallbacks, [&engine](auto& data) {
return data.second.fromEngine == engine;
});
return true;
}
Local<Value> convertResult(DynamicCommand::Result const& result) {
if (!result.isSet)
return Local<Value>(); // null
switch (result.type) {
case DynamicCommand::ParameterType::Bool:
return Boolean::newBoolean(result.getRaw<bool>());
case DynamicCommand::ParameterType::Int:
return Number::newNumber(result.getRaw<int>());
case DynamicCommand::ParameterType::Float:
return Number::newNumber(result.getRaw<float>());
case DynamicCommand::ParameterType::String:
return String::newString(result.getRaw<std::string>());
case DynamicCommand::ParameterType::Actor: {
auto arr = Array::newArray();
for (auto i : result.get<std::vector<Actor*>>()) {
arr.add(EntityClass::newEntity(i));
}
return arr;
}
case DynamicCommand::ParameterType::Player: {
auto arr = Array::newArray();
for (auto i : result.get<std::vector<Player*>>()) {
arr.add(PlayerClass::newPlayer(i));
}
return arr;
}
case DynamicCommand::ParameterType::BlockPos: {
auto dim = result.origin->getDimension();
return IntPos::newPos(result.get<BlockPos>(), dim ? (int)dim->getDimensionId() : -1);
}
case DynamicCommand::ParameterType::Vec3: {
auto dim = result.origin->getDimension();
return FloatPos::newPos(result.get<Vec3>(), dim ? (int)dim->getDimensionId() : -1);
}
case DynamicCommand::ParameterType::Message:
return String::newString(result.getRaw<CommandMessage>().getMessage(*result.origin));
case DynamicCommand::ParameterType::RawText:
return String::newString(result.getRaw<std::string>());
case DynamicCommand::ParameterType::JsonValue:
return String::newString(result.getRaw<Json::Value>().toStyledString());//这里可能会有问题原本使用JsonHelpers::serialize
case DynamicCommand::ParameterType::Item:
return ItemClass::newItem(new ItemStack(result.getRaw<CommandItem>().createInstance(1, 1, nullptr, true).value_or(ItemInstance::EMPTY_ITEM)));
case DynamicCommand::ParameterType::Block:
return BlockClass::newBlock(const_cast<Block*>(result.getRaw<Block const*>()), const_cast<BlockPos*>(&BlockPos::MIN), -1);
case DynamicCommand::ParameterType::Effect:
return String::newString(result.getRaw<MobEffect const*>()->getResourceName());
case DynamicCommand::ParameterType::Enum:
return String::newString(result.getRaw<std::string>());
case DynamicCommand::ParameterType::SoftEnum:
return String::newString(result.getRaw<std::string>());
case DynamicCommand::ParameterType::Command:
return String::newString(result.getRaw<std::unique_ptr<Command>>()->getCommandName());
case DynamicCommand::ParameterType::ActorType:
return String::newString(result.getRaw<ActorDefinitionIdentifier const*>()->getCanonicalName());
default:
return Local<Value>(); // null
break;
}
}
template <typename T>
std::enable_if_t<std::is_enum_v<T>, T> parseEnum(Local<Value> const& value) {
if (value.isString()) {
auto tmp = magic_enum::enum_cast<T>(value.toStr());
if (!tmp.has_value())
throw std::runtime_error("Unable to parse Enum value");
return tmp.value();
} else if (value.isNumber()) {
return (T)value.toInt();
}
throw std::runtime_error("Unable to parse Enum value");
}
//////////////////// MC APIs ////////////////////
Local<Value> McClass::runcmd(const Arguments& args) {
CHECK_ARGS_COUNT(args, 1)
CHECK_ARG_TYPE(args[0], ValueKind::kString)
try {
return Boolean::newBoolean(Level::executeCommand(args[0].asString().toString()));
}
CATCH("Fail in RunCmd!")
}
Local<Value> McClass::runcmdEx(const Arguments& args) {
CHECK_ARGS_COUNT(args, 1)
CHECK_ARG_TYPE(args[0], ValueKind::kString)
try {
std::pair<bool, string> result = Level::executeCommandEx(args[0].asString().toString());
Local<Object> resObj = Object::newObject();
resObj.set("success", result.first);
resObj.set("output", result.second);
return resObj;
}
CATCH("Fail in RunCmdEx!")
}
// name, description, permission, flag, alias
Local<Value> McClass::newCommand(const Arguments& args) {
CHECK_ARGS_COUNT(args, 2);
CHECK_ARG_TYPE(args[0], ValueKind::kString);
CHECK_ARG_TYPE(args[1], ValueKind::kString);
try {
auto name = args[0].toStr();
auto instance = DynamicCommand::getInstance(name);
if (instance) {
logger.info("Dynamic command {} already exists, changes will not be applied except for setOverload!", name);
return CommandClass::newCommand(const_cast<std::add_pointer_t<std::remove_cv_t<std::remove_pointer_t<decltype(instance)>>>>(instance));
}
auto desc = args[1].toStr();
CommandPermissionLevel permission = CommandPermissionLevel::GameMasters;
CommandFlag flag = {(CommandFlagValue)0x80};
std::string alias = "";
if (args.size() > 2) {
permission = parseEnum<CommandPermissionLevel>(args[2]);
if (args.size() > 3) {
CHECK_ARG_TYPE(args[3], ValueKind::kNumber);
flag = {(CommandFlagValue)args[3].toInt()};
if (args.size() > 4) {
CHECK_ARG_TYPE(args[4], ValueKind::kString);
alias = args[4].toStr();
}
}
}
auto command = DynamicCommand::createCommand(name, desc, permission, flag);
if (command) {
if (!alias.empty())
command->setAlias(alias);
return CommandClass::newCommand(std::move(command));
} else {
return Boolean::newBoolean(false);
}
}
CATCH("Fail in newCommand!")
}
//////////////////// Command APIs ////////////////////
CommandClass::CommandClass(std::unique_ptr<DynamicCommandInstance>&& p)
: ScriptClass(ScriptClass::ConstructFromCpp<CommandClass>{})
, uptr(std::move(p))
, ptr(uptr.get())
, registered(false){};
CommandClass::CommandClass(DynamicCommandInstance* p)
: ScriptClass(ScriptClass::ConstructFromCpp<CommandClass>{})
, uptr()
, ptr(p)
, registered(true){};
Local<Object> CommandClass::newCommand(std::unique_ptr<DynamicCommandInstance>&& p) {
auto newp = new CommandClass(std::move(p));
return newp->getScriptObject();
}
Local<Object> CommandClass::newCommand(DynamicCommandInstance* p) {
auto newp = new CommandClass(p);
return newp->getScriptObject();
}
Local<Value> CommandClass::getName() {
try {
return String::newString(get()->getCommandName());
}
CATCH("Fail in getCommandName!")
}
Local<Value> CommandClass::setAlias(const Arguments& args) {
CHECK_ARGS_COUNT(args, 1)
CHECK_ARG_TYPE(args[0], ValueKind::kString)
try {
if (registered)
return Boolean::newBoolean(true); // TODO
return Boolean::newBoolean(get()->setAlias(args[0].toStr()));
}
CATCH("Fail in setAlias!")
}
// string, vector<string>
Local<Value> CommandClass::setEnum(const Arguments& args) {
CHECK_ARGS_COUNT(args, 2)
CHECK_ARG_TYPE(args[0], ValueKind::kString)
CHECK_ARG_TYPE(args[1], ValueKind::kArray)
try {
if (registered)
return Local<Value>(); // TODO
auto enumName = args[0].toStr();
auto enumArr = args[1].asArray();
if (enumArr.size() == 0 || !enumArr.get(0).isString())
return Local<Value>();
vector<string> enumValues;
for (int i = 0; i < enumArr.size(); ++i) {
enumValues.push_back(enumArr.get(i).toStr());
}
return String::newString(get()->setEnum(enumName, std::move(enumValues)));
}
CATCH("Fail in setEnum!")
}
// name, type, optional, description, identifier, option
// name, type, description, identifier, option
// name, type, optional, description, option
// name, type, description, option
Local<Value> CommandClass::newParameter(const Arguments& args) {
CHECK_ARGS_COUNT(args, 2);
CHECK_ARG_TYPE(args[0], ValueKind::kString);
try {
if (registered)
return Boolean::newBoolean(true); // TODO
auto name = args[0].toStr();
DynamicCommand::ParameterType type = parseEnum<DynamicCommand::ParameterType>(args[1]);
std::string description = "";
bool optional = false;
std::string identifier = "";
size_t index = 2;
CommandParameterOption option = (CommandParameterOption)0;
if (args.size() > index && args[index].isBoolean())
optional = args[index++].asBoolean().value();
if (args.size() > index && args[index].isString())
description = args[index++].toStr();
if (args.size() > index && args[index].isString())
identifier = args[index++].toStr();
if (args.size() > index && args[index].isNumber())
option = (CommandParameterOption)args[index++].toInt();
if (index != args.size())
throw std::runtime_error("Error Argument in newParameter");
return Number::newNumber((int64_t)get()->newParameter(name, type, optional, description, identifier, option).index);
}
CATCH("Fail in newParameter!")
}
// name, type, description, identifier, option
// name, type, description, option
Local<Value> CommandClass::mandatory(const Arguments& args) {
CHECK_ARGS_COUNT(args, 2);
CHECK_ARG_TYPE(args[0], ValueKind::kString);
try {
if (registered)
return Boolean::newBoolean(true); // TODO
auto name = args[0].toStr();
DynamicCommand::ParameterType type = parseEnum<DynamicCommand::ParameterType>(args[1]);
std::string description = "";
bool optional = false;
std::string identifier = "";
size_t index = 2;
CommandParameterOption option = (CommandParameterOption)0;
if (args.size() > index && args[index].isString())
description = args[index++].toStr();
if (args.size() > index && args[index].isString())
identifier = args[index++].toStr();
if (args.size() > index && args[index].isNumber())
option = (CommandParameterOption)args[index++].toInt();
if (index != args.size())
throw std::runtime_error("Error Argument in newParameter");
return Number::newNumber((int64_t)get()->newParameter(name, type, optional, description, identifier, option).index);
}
CATCH("Fail in newParameter!")
}
// name, type, description, identifier, option
// name, type, description, option
Local<Value> CommandClass::optional(const Arguments& args) {
CHECK_ARGS_COUNT(args, 2);
CHECK_ARG_TYPE(args[0], ValueKind::kString);
try {
if (registered)
return Boolean::newBoolean(true); // TODO
auto name = args[0].toStr();
DynamicCommand::ParameterType type = parseEnum<DynamicCommand::ParameterType>(args[1]);
std::string description = "";
bool optional = true;
std::string identifier = "";
size_t index = 2;
CommandParameterOption option = (CommandParameterOption)0;
if (args.size() > index && args[index].isString())
description = args[index++].toStr();
if (args.size() > index && args[index].isString())
identifier = args[index++].toStr();
if (args.size() > index && args[index].isNumber())
option = (CommandParameterOption)args[index++].toInt();
if (index != args.size())
throw std::runtime_error("Error Argument in newParameter");
return Number::newNumber((int64_t)get()->newParameter(name, type, optional, description, identifier, option).index);
}
CATCH("Fail in newParameter!")
}
// vector<identifier>
// vector<index>
Local<Value> CommandClass::addOverload(const Arguments& args) {
try {
if (registered)
return Boolean::newBoolean(true); // TODO
auto command = get();
if (args.size() == 0)
return Boolean::newBoolean(command->addOverload(std::vector<DynamicCommandInstance::ParameterIndex>{}));
if (args[0].isNumber()) {
std::vector<DynamicCommandInstance::ParameterIndex> params;
for (int i = 0; i < args.size(); ++i) {
CHECK_ARG_TYPE(args[i], ValueKind::kNumber);
params.emplace_back(command, (size_t)args[i].asNumber().toInt64());
}
return Boolean::newBoolean(command->addOverload(std::move(params)));
} else if (args[0].isString()) {
std::vector<std::string> params;
for (int i = 0; i < args.size(); ++i) {
CHECK_ARG_TYPE(args[i], ValueKind::kString);
params.emplace_back(args[i].toStr());
}
return Boolean::newBoolean(command->addOverload(std::move(params)));
} else if (args[0].isArray()) {
auto arr = args[0].asArray();
if (arr.size() == 0)
return Boolean::newBoolean(command->addOverload(std::vector<DynamicCommandInstance::ParameterIndex>{}));
if (arr.get(0).isNumber()) {
std::vector<DynamicCommandInstance::ParameterIndex> params;
for (int i = 0; i < arr.size(); ++i) {
CHECK_ARG_TYPE(arr.get(i), ValueKind::kNumber);
params.emplace_back(command, (size_t)arr.get(i).asNumber().toInt64());
}
return Boolean::newBoolean(command->addOverload(std::move(params)));
} else if (arr.get(0).isString()) {
std::vector<std::string> params;
for (int i = 0; i < arr.size(); ++i) {
CHECK_ARG_TYPE(arr.get(i), ValueKind::kString);
params.emplace_back(arr.get(i).toStr());
}
return Boolean::newBoolean(command->addOverload(std::move(params)));
}
}
LOG_WRONG_ARG_TYPE();
return Local<Value>();
}
CATCH("Fail in addOverload!")
}
void onExecute(DynamicCommand const& command, CommandOrigin const& origin, CommandOutput& output,
std::unordered_map<std::string, DynamicCommand::Result>& results) {
auto instance = command.getInstance();
auto& commandName = instance->getCommandName();
if (localShareData->commandCallbacks.find(commandName) == localShareData->commandCallbacks.end()) {
logger.warn("Command {} failed to execute, is the plugin unloaded?", commandName);
return;
}
EngineScope enter(localShareData->commandCallbacks[commandName].fromEngine);
try {
Local<Object> args = Object::newObject();
auto cmd = CommandClass::newCommand(const_cast<DynamicCommandInstance*>(instance));
auto ori = CommandOriginClass::newCommandOrigin(&origin);
auto outp = CommandOutputClass::newCommandOutput(&output);
for (auto& [name, param] : results)
args.set(name, convertResult(param));
localShareData->commandCallbacks[commandName].func.get().call({}, cmd, ori, outp, args);
}
CATCH_WITHOUT_RETURN("Fail in executing command \"" + commandName + "\"!")
}
// not complete
void onExecute2(DynamicCommand const& command, CommandOrigin const& origin, CommandOutput& output,
std::unordered_map<std::string, DynamicCommand::Result>& results) {
auto instance = command.getInstance();
auto& commandName = instance->getCommandName();
if (localShareData->commandCallbacks.find(commandName) == localShareData->commandCallbacks.end()) {
logger.warn("Command {} failed to execute, is the plugin unloaded?", commandName);
return;
}
EngineScope enter(localShareData->commandCallbacks[commandName].fromEngine);
try {
// auto ctx = CommandContextClass::newCommandContext(&command, &origin, &output, &results);
// Local<Object> args = Object::newObject();
// for (auto& [name, param] : results)
// args.set(name, convertResult(param));
// localShareData->commandCallbacks[commandName].func.get().call({}, ctx, args);
}
CATCH_WITHOUT_RETURN("Fail in executing command \"" + commandName + "\"!")
}
// function (command, origin, output, results){}
Local<Value> CommandClass::setCallback(const Arguments& args) {
CHECK_ARGS_COUNT(args, 1);
CHECK_ARG_TYPE(args[0], ValueKind::kFunction);
try {
auto func = args[0].asFunction();
DynamicCommandInstance* command = get();
auto& commandName = command->getCommandName();
localShareData->commandCallbacks[commandName] = {EngineScope::currentEngine(), 0, script::Global<Function>(func)};
if (registered)
return Boolean::newBoolean(true);
get()->setCallback(onExecute);
return Boolean::newBoolean(true);
}
CATCH("Fail in setCallback!")
}
// setup(Function<Command, Origin, Output, Map<String, Any>>)
Local<Value> CommandClass::setup(const Arguments& args) {
try {
if (args.size() > 0) {
setCallback(args);
}
if (registered)
return Boolean::newBoolean(true);
return Boolean::newBoolean(DynamicCommand::setup(std::move(uptr)));
}
CATCH("Fail in setup!")
}
Local<Value> CommandClass::isRegistered() {
return Boolean::newBoolean(registered);
}
Local<Value> CommandClass::toString(const Arguments& args) {
try {
return String::newString(fmt::format("<Command({})>", get()->getCommandName()));
}
CATCH("Fail in toString!");
}
Local<Value> CommandClass::setSoftEnum(const Arguments& args) {
CHECK_ARGS_COUNT(args, 2);
CHECK_ARG_TYPE(args[0], ValueKind::kString);
CHECK_ARG_TYPE(args[1], ValueKind::kArray);
try {
auto name = args[0].toStr();
auto enums = parseStringList(args[1].asArray());
return String::newString(get()->setSoftEnum(name, std::move(enums)));
}
CATCH("Fail in setSoftEnum!");
}
Local<Value> CommandClass::addSoftEnumValues(const Arguments& args) {
CHECK_ARGS_COUNT(args, 2);
CHECK_ARG_TYPE(args[0], ValueKind::kString);
CHECK_ARG_TYPE(args[1], ValueKind::kArray);
try {
auto name = args[0].toStr();
auto enums = parseStringList(args[1].asArray());
return Boolean::newBoolean(get()->addSoftEnumValues(name, std::move(enums)));
}
CATCH("Fail in addSoftEnumValues!");
}
Local<Value> CommandClass::removeSoftEnumValues(const Arguments& args) {
CHECK_ARGS_COUNT(args, 2);
CHECK_ARG_TYPE(args[0], ValueKind::kString);
CHECK_ARG_TYPE(args[1], ValueKind::kArray);
try {
auto name = args[0].toStr();
auto enums = parseStringList(args[1].asArray());
return Boolean::newBoolean(get()->removeSoftEnumValues(name, std::move(enums)));
}
CATCH("Fail in removeSoftEnumValues!");
}
Local<Value> CommandClass::getSoftEnumValues(const Arguments& args) {
CHECK_ARGS_COUNT(args, 1);
CHECK_ARG_TYPE(args[0], ValueKind::kString);
try {
auto name = args[0].toStr();
return getStringArray(get()->getSoftEnumValues(name));
}
CATCH("Fail in getSoftEnumValues");
}
Local<Value> CommandClass::getSoftEnumNames(const Arguments& args) {
try {
return getStringArray(get()->getSoftEnumNames());
}
CATCH("Fail in getSoftEnumNames");
}