#pragma once #include "Global.h" class Actor; #define USE_PARSE_ENUM_STRING //#define ENABLE_PARAMETER_TYPE_POSTFIX #include "llapi/mc/Command.hpp" #include "llapi/mc/CommandOrigin.hpp" #include "llapi/mc/CommandOutput.hpp" #include "llapi/mc/CommandParameterData.hpp" #include "llapi/mc/CommandRegistry.hpp" #include "llapi/mc/CommandSelector.hpp" #include "llapi/mc/CommandPosition.hpp" #include "llapi/utils/WinHelper.h" #include "magic_enum/magic_enum.hpp" /////////////////////////////////////////////////////// // Dynamic Command Registry // // [Example] // ## One Example: // // Direct setup of dynamic command with necessary information // using ParamType = DynamicCommand::ParameterType; // using Param = DynamicCommand::ParameterData; // DynamicCommand::setup( // "testenum", // command name // "dynamic command", // command description // { // // enums{enumName, {values...}} // {"TestEnum1", {"add", "remove"}}, // {"TestEnum2", {"list"}}, // }, // { // // parameters(type, name, [optional], [enumOptions(also enumName)], [identifier]) // // identifier: used to identify unique parameter data, if idnetifier is not set, // // it is set to be the same as enumOptions or name (identifier = enumOptions.empty() ? name:enumOptions) // Param("testEnum", ParamType::Enum, "TestEnum1"), // Param("testEnum", ParamType::Enum, "TestEnum2"), // Param("testInt", ParamType::Int, true), // }, // { // // overloads{ (type == Enum ? enumOptions : name) ...} // {"TestEnum1", "testInt"}, // testenum [testInt] // {"TestEnum2"}, // testenum // }, // // dynamic command callback // [](DynamicCommand const& command, CommandOrigin const& origin, CommandOutput& output, // std::unordered_map& results) { // auto& action = results["testEnum"].get(); // switch (do_hash(action.c_str())) // { // case do_hash("add"): // if (results["testInt"].isSet) // output.success(fmt::format("add {}", results["testInt"].getRaw())); // else // output.success("add nothing"); // break; // case do_hash("remove"): // if (results["testInt"].isSet) // output.success(fmt::format("remove {}", results["testInt"].getRaw())); // else // output.success("remove nothing"); // break; // case do_hash("list"): // output.success("list"); // break; // default: // break; // } // }, // CommandPermissionLevel::GameMasters); // // ## Another Example // using ParamType = DynamicCommand::ParameterType; // // create a dynamic command // auto command = DynamicCommand::createCommand("testcmd", "dynamic command", CommandPermissionLevel::GameMasters); // // auto& optionsAdd = command->setEnum("TestOperation1", {"add", "remove"}); // auto& optionsList = command->setEnum("TestOperation2", {"list"}); // // command->mandatory("testEnum", ParamType::Enum, optionsAdd, CommandParameterOption::EnumAutocompleteExpansion); // command->mandatory("testEnum", ParamType::Enum, optionsList, CommandParameterOption::EnumAutocompleteExpansion); // command->mandatory("testString", ParamType::String); // // command->addOverload({optionsAdd, "testString"}); // dyncmd // command->addOverload({"TestOperation2"}); // dyncmd // // command->setCallback([](DynamicCommand const& command, CommandOrigin const& origin, CommandOutput& output, std::unordered_map& results) { // switch (do_hash(results["testEnum"].getRaw().c_str())) // { // case do_hash("add"): // output.success(fmt::format("Add - {}", results["testString"].getRaw())); // break; // case do_hash("remove"): // output.success(fmt::format("Remove - {}", results["testString"].getRaw())); // break; // case do_hash("list"): // output.success("List"); // break; // default: // break; // } // }); // // do not forget to setup the command instance // DynamicCommand::setup(std::move(command)); // ///////////////////////////////////////////////////// struct DCCallback; struct DCArgs; typedef union DCValue_ DCValue; class DynamicCommandInstance; class CommandMessage; class CommandOutput; class CommandRegistry; class Player; #define AllResultType bool const*, int const*, float const*, std::string const*, CommandSelector const*, CommandSelector const*, CommandPosition const*, CommandPositionFloat const*, CommandRawText const*, CommandMessage const*, Json::Value const*, CommandItem const*, Block const* const*, MobEffect const* const*, ActorDefinitionIdentifier const* const*, std::unique_ptr const* class DynamicCommand : public Command { template static constexpr bool is_one_of_v = std::_Meta_find_unique_index, std::add_pointer_t>>::value < sizeof...(_Types); template static constexpr bool is_supported_result_type_v = is_one_of_v<_Ty, AllResultType>; template using enable_if_supported_t = std::enable_if_t, Type>; public: enum class ParameterType { Bool, // bool Int, // int Float, // float String, // std::string Actor, // CommandSelector Player, // CommandSelector BlockPos, // CommandPosition Vec3, // CommandPositionFloat RawText, // CommandRawText Message, // CommandMessage JsonValue, // Json::Value Item, // CommandItem Block, // Block const* Effect, // MobEffect const* Enum, // ENUM SoftEnum, // SOFT_ENUM ActorType, // ActorDefinitionIdentifier const* Command, // std::unique_ptr WildcardSelector, // WildcardCommandSelector #ifdef ENABLE_PARAMETER_TYPE_POSTFIX Postfix, // int? #endif // ENABLE_PARAMETER_TYPE_POSTFIX }; struct ParameterPtr; struct Result { ParameterType const type; size_t const offset; bool const isSet; DynamicCommand const* command; DynamicCommandInstance const* instance; CommandOrigin const* origin; LIAPI Result(ParameterPtr const* ptr, DynamicCommand const* command, CommandOrigin const* origin, DynamicCommandInstance const* instance = nullptr); LIAPI Result(); LIAPI std::string const& getEnumValue() const; LIAPI ParameterType getType() const; LIAPI std::string getName() const; LIAPI std::string toDebugString() const; LIAPI DynamicCommandInstance const* getInstance() const; template inline enable_if_supported_t getRaw() const { #ifdef USE_PARSE_ENUM_STRING if (type == ParameterType::Enum) { auto& val = dAccess>(command, offset); if constexpr (std::is_same_v, int> || std::is_enum_v) { return static_cast(val.second); } else if constexpr (std::is_same_v, std::string>) { return static_cast(val.first); } } #else if constexpr (std::is_same_v, std::string>) { if (type == ParameterType::Enum) return getEnumValue(); } #endif // USE_PARSE_ENUM_STRING if (checkTempateType(type)) return dAccess(command, offset); throw std::runtime_error(fmt::format("Raw type not match, parameter Type: {}, data type: {}", magic_enum::enum_name(type), typeid(T).name())); } template inline enable_if_supported_t value_or(T const& defaultValue) { if (isSet) return getRaw(); return defaultValue; } template inline std::conditional_t, std::add_lvalue_reference_t>>, T> get() const { static_assert(is_supported_result_type_v || (std::is_lvalue_reference_v && is_supported_result_type_v>), "Unsupported Result Type in " __FUNCTION__); if constexpr (std::is_lvalue_reference_v) return getRaw>(); else return getRaw(); } template <> inline std::vector get>() const { if (type == ParameterType::Player) { auto players = get>(); std::vector actors(players.size()); std::transform(players.begin(), players.end(), actors.begin(), [](Player* player) { return static_cast(player); }); return actors; } std::vector rtn; for (auto& result : getRaw>().results(*origin)) { rtn.push_back(result); } return rtn; } template <> inline std::vector get>() const { std::vector rtn; for (auto& result : getRaw>().results(*origin)) { rtn.push_back(result); } return rtn; } template <> inline BlockPos get() const { auto& pos = getRaw(); return pos.getBlockPos(*origin, Vec3::ZERO); } template <> inline Vec3 get() const { auto& pos = getRaw(); return pos.getPosition(*origin, Vec3::ZERO); } }; struct ParameterPtr { ParameterType type; private: size_t offset = -1; friend struct Result; public: LIAPI ParameterPtr(ParameterType type, size_t offset); LIAPI bool isValueSet(DynamicCommand const* command) const; LIAPI Result getResult(DynamicCommand const* command, CommandOrigin const* origin) const; inline size_t getOffset() const { return offset; } }; struct ParameterData { protected: DynamicCommand::ParameterType type; size_t offset = -1; std::string name; std::string description; std::string identifier; bool optional = false; CommandParameterOption option; public: ParameterData() = delete; LIAPI ParameterData(ParameterData const&); LIAPI ParameterData(std::string const& name, DynamicCommand::ParameterType type, bool optional = false, std::string const& enumOptions = "", std::string const& identifier = "", CommandParameterOption parameterOption = (CommandParameterOption)0); LIAPI ParameterData(std::string const& name, DynamicCommand::ParameterType type, std::string const& enumOptions = "", std::string const& identifier = "", CommandParameterOption parameterOption = (CommandParameterOption)0); LIAPI CommandParameterData makeParameterData() const; friend class DynamicCommandInstance; friend class DynamicCommand; template inline static constexpr CommandParameterDataType getCommandParameterDataType() { if constexpr (type == ParameterType::Enum) return CommandParameterDataType::ENUM; else if constexpr (type == ParameterType::SoftEnum) return CommandParameterDataType::SOFT_ENUM; // else if constexpr (type == ParameterType::Postfix) // return CommandParameterDataType::POSIFIX; else return CommandParameterDataType::NORMAL; } template CommandParameterData makeParameterData() const { CommandParameterData param{ type == ParameterType::Enum ? typeid_t::count++ : type_id(), type == ParameterType::Enum ? &CommandRegistry::fakeParse : CommandRegistry::getParseFn(), name, getCommandParameterDataType(), description == "" ? nullptr : description.data(), (int)offset, optional, (int)offset + std::max(8, (int)sizeof(T))}; param.addOptions(option); // logger.warn(Global->describe(param)); return std::move(param); } inline void setOptional(bool optional) { this->optional = optional; } inline bool setEnumOptions(std::string const& enumOptions) { if (type != DynamicCommand::ParameterType::Enum && type != DynamicCommand::ParameterType::SoftEnum) return false; this->description = enumOptions; return true; } inline bool setParameterOption(CommandParameterOption parameterOption) { this->option = parameterOption; } inline ParameterData(std::string const& name, ParameterType type, const char* enumOptions = "", std::string const& identifer = "", CommandParameterOption parameterOption = (CommandParameterOption)0) : ParameterData(name, type, (std::string const&)enumOptions, identifer, parameterOption){}; }; using CallBackFn = std::function& results)>; using BuilderFn = std::unique_ptr (*)(); private: template inline static enable_if_supported_t<_Ty, bool> checkTempateType(ParameterType type) { switch (type) { case ParameterType::Bool: return std::is_same_v>; case ParameterType::Int: return std::is_same_v>; case ParameterType::Float: return std::is_same_v>; case ParameterType::String: return std::is_same_v>; case ParameterType::Actor: return std::is_same_v, std::remove_cv_t<_Ty>>; case ParameterType::Player: return std::is_same_v, std::remove_cv_t<_Ty>>; case ParameterType::BlockPos: case ParameterType::Vec3: return std::is_same_v> || std::is_same_v>; case ParameterType::RawText: return std::is_same_v> || std::is_same_v>; case ParameterType::Message: return std::is_same_v>; case ParameterType::JsonValue: return std::is_same_v>; case ParameterType::Item: return std::is_same_v>; case ParameterType::Block: return std::is_same_v>; case ParameterType::Effect: return std::is_same_v>; // case ParameterType::Position: // return std::is_same_v> || std::is_same_v> || std::is_same_v>; case ParameterType::Enum: return std::is_same_v> || std::is_same_v> || std::is_enum_v<_Ty>; case ParameterType::SoftEnum: return std::is_same_v>; case ParameterType::ActorType: return std::is_same_v>; case ParameterType::Command: return std::is_same_v, std::remove_cv_t<_Ty>>; default: return false; break; } return false; } LIAPI static char builderCallbackHanler(DCCallback* cb, DCArgs* args, DCValue* result, void* userdata); LIAPI static std::unique_ptr* commandBuilder(std::unique_ptr* rtn, std::string name); LIAPI static DynamicCommandInstance* _setup(std::unique_ptr commandInstance); public: static bool onServerCommandsRegister(CommandRegistry& registry); friend class DynamicCommandInstance; public: /*0*/ virtual ~DynamicCommand(); /*1*/ virtual void execute(class CommandOrigin const& origin, class CommandOutput& output) const; LIAPI static std::unique_ptr createCommand(std::string const& name, std::string const& description, CommandPermissionLevel permission = CommandPermissionLevel::GameMasters, CommandFlag flag1 = {(CommandFlagValue)4}, CommandFlag flag2 = {(CommandFlagValue)0}, HMODULE handle = GetCurrentModule()); LIAPI static std::unique_ptr createCommand( std::string const& name, std::string const& description, std::unordered_map>&& enums, std::vector&& params, std::vector>&& overloads, CallBackFn callback, CommandPermissionLevel permission = CommandPermissionLevel::GameMasters, CommandFlag flag1 = {(CommandFlagValue)0x40}, CommandFlag flag2 = {(CommandFlagValue)0}, HMODULE handle = GetCurrentModule()); LIAPI static DynamicCommandInstance const* setup(std::unique_ptr commandInstance); inline static DynamicCommandInstance const* setup( std::string const& name, std::string const& description, std::unordered_map>&& enums, std::vector&& params, std::vector>&& overloads, CallBackFn callback, CommandPermissionLevel permission = CommandPermissionLevel::GameMasters, CommandFlag flag1 = {(CommandFlagValue)0x40}, CommandFlag flag2 = {(CommandFlagValue)0}, HMODULE handle = GetCurrentModule()) { return setup(createCommand(name, description, std::move(enums), std::move(params), std::move(overloads), std::move(callback), permission, flag1, flag2, handle)); }; // Experiment LIAPI static bool unregisterCommand(std::string const& name); LIAPI static bool updateAvailableCommands(); LIAPI DynamicCommandInstance const* getInstance() const; LIAPI static DynamicCommandInstance const* getInstance(std::string const& commandName); }; class DynamicCommandInstance { public: struct ParameterIndex { DynamicCommandInstance* instance; size_t index; ParameterIndex(DynamicCommandInstance* instance, size_t index) : instance(instance) , index(index){}; inline operator size_t() const { return index; } inline DynamicCommand::ParameterData& operator->() { return instance->parameterDatas.at(index); } inline bool isValid() const { size_t size = instance->parameterDatas.size(); return index >= 0 && index < size; } }; private: std::string name; std::string alias; std::unique_ptr description; CommandPermissionLevel permission; CommandFlag flag; DynamicCommand::BuilderFn builder = nullptr; public: // Parameter Pointers to DynamicCommand Extra Part size_t commandSize = sizeof(DynamicCommand); std::unordered_map parameterPtrs = {}; // Use unique_ptr to keep the address of enumName.c_str() immutable std::vector> enumNames = {}; std::vector enumValues = {}; // unordered_map{ enumName, pair{ enumIndex, enumSize } } std::unordered_map> enumRanges = {}; //// unordered_map{ enumName, pair{ enumIndex, enumConstraint } } // std::unordered_map> enumConstraints = {}; // SoftEnum mutable std::unordered_map> softEnums; std::vector parameterDatas = {}; private: std::vector> overloads = {}; // indices of parameter instance mutable DynamicCommand::CallBackFn callback = nullptr; HMODULE handle = nullptr; friend class DynamicCommand; LIAPI DynamicCommandInstance(std::string const& name, std::string const& description, CommandPermissionLevel permission = CommandPermissionLevel::GameMasters, CommandFlag flag = {(CommandFlagValue)0x40}, HMODULE handle = GetCurrentModule()); LIAPI bool setBuilder(DynamicCommand::BuilderFn builder); LIAPI DynamicCommand::BuilderFn initCommandBuilder(); LIAPI std::vector buildOverload(std::vector const& overload); public: virtual ~DynamicCommandInstance(); LIAPI static std::unique_ptr create(std::string const& name, std::string const& description, CommandPermissionLevel permission, CommandFlag flag, HMODULE handle = GetCurrentModule()); LIAPI std::string const& setEnum(std::string const& description, std::vector const& values); LIAPI std::string const& getEnumValue(int index) const; LIAPI ParameterIndex newParameter(DynamicCommand::ParameterData&& data); LIAPI ParameterIndex newParameter(std::string const& name, DynamicCommand::ParameterType type, bool optional = false, std::string const& description = "", std::string const& identifier = "", CommandParameterOption parameterOption = (CommandParameterOption)0); LIAPI ParameterIndex findParameterIndex(std::string const& param); LIAPI ParameterIndex mandatory(std::string const& name, DynamicCommand::ParameterType type, std::string const& description, std::string const& identifier, CommandParameterOption parameterOption = (CommandParameterOption)0); LIAPI ParameterIndex mandatory(std::string const& name, DynamicCommand::ParameterType type, std::string const& description, CommandParameterOption parameterOption = (CommandParameterOption)0); LIAPI ParameterIndex mandatory(std::string const& name, DynamicCommand::ParameterType type, CommandParameterOption parameterOption = CommandParameterOption::None); LIAPI ParameterIndex optional(std::string const& name, DynamicCommand::ParameterType type, std::string const& description, std::string const& identifier, CommandParameterOption parameterOption = (CommandParameterOption)0); LIAPI ParameterIndex optional(std::string const& name, DynamicCommand::ParameterType type, std::string const& description, CommandParameterOption parameterOption = (CommandParameterOption)0); LIAPI ParameterIndex optional(std::string const& name, DynamicCommand::ParameterType type, CommandParameterOption parameterOption = CommandParameterOption::None); // LIAPI bool addOverload(); LIAPI bool addOverload(std::vector&& params); LIAPI bool addOverload(std::vector&& params); LIAPI bool addOverload(std::vector&& params); LIAPI bool addOverload(std::vector&& params); LIAPI bool setAlias(std::string const& alias); LIAPI void setCallback(DynamicCommand::CallBackFn&& callback) const; LIAPI void removeCallback() const; // LIAPI static bool updateSoftEnum(std::string const& name = "") const; LIAPI std::string setSoftEnum(std::string const& name, std::vector const& values) const; LIAPI bool addSoftEnumValues(std::string const& name, std::vector const& values) const; LIAPI bool removeSoftEnumValues(std::string const& name, std::vector const& values) const; LIAPI static std::vector getSoftEnumValues(std::string const& name); LIAPI static std::vector getSoftEnumNames(); template inline std::enable_if_t::value, ParameterIndex> toIndex(T const& arg) { return findParameterIndex(arg); } template inline std::enable_if_t::value, ParameterIndex> toIndex(T const& arg) = delete; template <> inline ParameterIndex toIndex(ParameterIndex const& arg) { return arg; } template <> inline ParameterIndex toIndex(DynamicCommand::ParameterData const& arg) { return newParameter(DynamicCommand::ParameterData(arg)); } template inline bool addOverload(Args const&... args) { return addOverload(std::vector{toIndex(args)...}); } template inline bool addOverload(std::initializer_list&& params) { return addOverload((std::vector)params); } LIAPI std::string const& getCommandName() const; inline ParameterIndex newParameter(std::string const& name, DynamicCommand::ParameterType type, const char* description, std::string const& identifier, CommandParameterOption parameterOption = (CommandParameterOption)0) { return newParameter(name, type, false, (std::string const&)description, identifier, parameterOption); }; inline bool hasRegistered() const { return DynamicCommand::getInstance(getCommandName()) != nullptr; }; inline void onExecute(DynamicCommand const& command, CommandOrigin const& origin, CommandOutput& output) const { }; };