//#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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //////////////////// Class Definition //////////////////// ClassDefine PermissionStaticBuilder = EnumDefineBuilder::build("PermType"); ClassDefine ParamTypeStaticBuilder = EnumDefineBuilder::build("ParamType"); ClassDefine ParamOptionStaticBuilder = EnumDefineBuilder::build("ParamOption"); ClassDefine CommandClassBuilder = defineClass("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 convertResult(DynamicCommand::Result const& result) { if (!result.isSet) return Local(); // null switch (result.type) { case DynamicCommand::ParameterType::Bool: return Boolean::newBoolean(result.getRaw()); case DynamicCommand::ParameterType::Int: return Number::newNumber(result.getRaw()); case DynamicCommand::ParameterType::Float: return Number::newNumber(result.getRaw()); case DynamicCommand::ParameterType::String: return String::newString(result.getRaw()); case DynamicCommand::ParameterType::Actor: { auto arr = Array::newArray(); for (auto i : result.get>()) { arr.add(EntityClass::newEntity(i)); } return arr; } case DynamicCommand::ParameterType::Player: { auto arr = Array::newArray(); for (auto i : result.get>()) { arr.add(PlayerClass::newPlayer(i)); } return arr; } case DynamicCommand::ParameterType::BlockPos: { auto dim = result.origin->getDimension(); return IntPos::newPos(result.get(), dim ? (int)dim->getDimensionId() : -1); } case DynamicCommand::ParameterType::Vec3: { auto dim = result.origin->getDimension(); return FloatPos::newPos(result.get(), dim ? (int)dim->getDimensionId() : -1); } case DynamicCommand::ParameterType::Message: return String::newString(result.getRaw().getMessage(*result.origin)); case DynamicCommand::ParameterType::RawText: return String::newString(result.getRaw()); case DynamicCommand::ParameterType::JsonValue: return String::newString(result.getRaw().toStyledString());//这里可能会有问题原本使用JsonHelpers::serialize case DynamicCommand::ParameterType::Item: return ItemClass::newItem(new ItemStack(result.getRaw().createInstance(1, 1, nullptr, true).value_or(ItemInstance::EMPTY_ITEM))); case DynamicCommand::ParameterType::Block: return BlockClass::newBlock(const_cast(result.getRaw()), const_cast(&BlockPos::MIN), -1); case DynamicCommand::ParameterType::Effect: return String::newString(result.getRaw()->getResourceName()); case DynamicCommand::ParameterType::Enum: return String::newString(result.getRaw()); case DynamicCommand::ParameterType::SoftEnum: return String::newString(result.getRaw()); case DynamicCommand::ParameterType::Command: return String::newString(result.getRaw>()->getCommandName()); case DynamicCommand::ParameterType::ActorType: return String::newString(result.getRaw()->getCanonicalName()); default: return Local(); // null break; } } template std::enable_if_t, T> parseEnum(Local const& value) { if (value.isString()) { auto tmp = magic_enum::enum_cast(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 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 McClass::runcmdEx(const Arguments& args) { CHECK_ARGS_COUNT(args, 1) CHECK_ARG_TYPE(args[0], ValueKind::kString) try { std::pair result = Level::executeCommandEx(args[0].asString().toString()); Local 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 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>>>(instance)); } auto desc = args[1].toStr(); CommandPermissionLevel permission = CommandPermissionLevel::GameMasters; CommandFlag flag = {(CommandFlagValue)0x80}; std::string alias = ""; if (args.size() > 2) { permission = parseEnum(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&& p) : ScriptClass(ScriptClass::ConstructFromCpp{}) , uptr(std::move(p)) , ptr(uptr.get()) , registered(false){}; CommandClass::CommandClass(DynamicCommandInstance* p) : ScriptClass(ScriptClass::ConstructFromCpp{}) , uptr() , ptr(p) , registered(true){}; Local CommandClass::newCommand(std::unique_ptr&& p) { auto newp = new CommandClass(std::move(p)); return newp->getScriptObject(); } Local CommandClass::newCommand(DynamicCommandInstance* p) { auto newp = new CommandClass(p); return newp->getScriptObject(); } Local CommandClass::getName() { try { return String::newString(get()->getCommandName()); } CATCH("Fail in getCommandName!") } Local 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 Local 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(); // TODO auto enumName = args[0].toStr(); auto enumArr = args[1].asArray(); if (enumArr.size() == 0 || !enumArr.get(0).isString()) return Local(); vector 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 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(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 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(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 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(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 // vector Local 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{})); if (args[0].isNumber()) { std::vector 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 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{})); if (arr.get(0).isNumber()) { std::vector 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 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(); } CATCH("Fail in addOverload!") } void onExecute(DynamicCommand const& command, CommandOrigin const& origin, CommandOutput& output, std::unordered_map& 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 args = Object::newObject(); auto cmd = CommandClass::newCommand(const_cast(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& 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 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 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(func)}; if (registered) return Boolean::newBoolean(true); get()->setCallback(onExecute); return Boolean::newBoolean(true); } CATCH("Fail in setCallback!") } // setup(Function>) Local 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 CommandClass::isRegistered() { return Boolean::newBoolean(registered); } Local CommandClass::toString(const Arguments& args) { try { return String::newString(fmt::format("", get()->getCommandName())); } CATCH("Fail in toString!"); } Local 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 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 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 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 CommandClass::getSoftEnumNames(const Arguments& args) { try { return getStringArray(get()->getSoftEnumNames()); } CATCH("Fail in getSoftEnumNames"); }