#pragma once #include "Global.h" #include "Utils/WinHelper.h" #include "third-party/Nlohmann/json.hpp" #include "MC/CompoundTag.hpp" #include "MC/Container.hpp" #include "MC/ItemStack.hpp" #include "MC/BlockInstance.hpp" #include "MC/VanillaDimensions.hpp" #include "MC/Player.hpp" #include "MC/Block.hpp" #include "MC/BlockActor.hpp" #define TEST_NEW_VALUE_TYPE /////////////////////////////////////////////////////// // Remote Call API // Mainly designed for scripting engines // Please call it in MC_SERVER thread or in ScheduleAPI // make sure the callback parameter type can be converted to json // // [Usage] // RemoteCall::exportAs("TestNameSpace", "strSize", [](std::string const& arg) -> int { return arg.size(); }); // // // in other plugin // auto strSize = RemoteCall::importAs<int(std::string const& arg)>("TestNameSpace", "strSize"); // logger.info("Size of str: {}", strSize("12345678")); // // // in js plugin // const strSize = ll.import("TestNameSpace", "strSize"); // logger.info(`Size of str: ${strSize("12345678")}`); // ///////////////////////////////////////////////////// namespace RemoteCall { #ifdef TEST_NEW_VALUE_TYPE // ..... struct NbtType { CompoundTag const* ptr = nullptr; bool own = false; NbtType(std::unique_ptr<CompoundTag> tag) : ptr(tag.release()) , own(true){}; NbtType(CompoundTag const* ptr) : ptr(ptr) , own(false){}; inline std::unique_ptr<CompoundTag> tryGetUniquePtr() { if (!own) return {}; own = false; auto uptr = std::unique_ptr<CompoundTag>(const_cast<CompoundTag*>(ptr)); ptr = nullptr; return std::move(uptr); } template <typename RTN> inline RTN get() = delete; template <> inline CompoundTag const* get() { return ptr; }; template <> inline CompoundTag* get() { return const_cast<CompoundTag*>(ptr); }; template <> inline std::unique_ptr<CompoundTag> get() { return tryGetUniquePtr(); }; }; struct ItemType { ItemStack const* ptr = nullptr; bool own = false; ItemType(std::unique_ptr<ItemStack> tag) : ptr(tag.release()) , own(true){}; ItemType(ItemStack const* ptr) : ptr(ptr) , own(false){}; inline std::unique_ptr<ItemStack> tryGetUniquePtr() { if (!own) return {}; own = false; auto uptr = std::unique_ptr<ItemStack>(const_cast<ItemStack*>(ptr)); ptr = nullptr; return std::move(uptr); } template <typename RTN> inline RTN get() = delete; template <> inline ItemStack const* get() { return ptr; }; template <> inline ItemStack* get() { return const_cast<ItemStack*>(ptr); }; template <> inline std::unique_ptr<ItemStack> get() { return tryGetUniquePtr(); }; }; struct BlockType { BlockInstance instance; BlockType(BlockInstance instance) : instance(instance){}; BlockType(Block const* ptr) : instance(BlockInstance::createBlockInstance(const_cast<Block*>(ptr), BlockPos::ZERO, -1)){}; template <typename RTN> inline RTN get() = delete; template <> inline Block const* get() { return instance.getBlock(); }; template <> inline BlockInstance get() { return instance; }; }; struct NumberType { __int64 i = 0; double f = 0; NumberType(__int64 i, double f) : i(i) , f(f){}; template <typename T> std::enable_if_t<std::is_integral_v<T> || std::is_floating_point_v<T>, NumberType&> operator=(T v) { i = static_cast<__int64>(v); f = static_cast<double>(v); } NumberType(double v) : i(static_cast<__int64>(v)) , f(static_cast<double>(v)){}; NumberType(float v) : i(static_cast<__int64>(v)) , f(static_cast<double>(v)){}; NumberType(__int64 v) : i(static_cast<__int64>(v)) , f(static_cast<double>(v)){}; NumberType(int v) : i(static_cast<__int64>(v)) , f(static_cast<double>(v)){}; NumberType(short v) : i(static_cast<__int64>(v)) , f(static_cast<double>(v)){}; NumberType(char v) : i(static_cast<__int64>(v)) , f(static_cast<double>(v)){}; NumberType(unsigned __int64 v) : i(static_cast<__int64>(v)) , f(static_cast<double>(v)){}; NumberType(unsigned int v) : i(static_cast<__int64>(v)) , f(static_cast<double>(v)){}; NumberType(unsigned short v) : i(static_cast<__int64>(v)) , f(static_cast<double>(v)){}; NumberType(unsigned char v) : i(static_cast<__int64>(v)) , f(static_cast<double>(v)){}; template <typename RTN> inline std::enable_if_t<std::is_integral_v<RTN>, RTN> get() { return static_cast<RTN>(i); }; template <typename RTN> inline std::enable_if_t<std::is_floating_point_v<RTN>, RTN> get() { return static_cast<RTN>(f); }; }; struct WorldPosType { Vec3 pos = Vec3::ZERO; int dimId = 3; // VanillaDimensions::Undefined; WorldPosType(Vec3 const& pos, int dimId = 3) : pos(pos) , dimId(dimId){}; WorldPosType(std::pair<Vec3, int> const& pos) : pos(pos.first) , dimId(pos.second){}; template <typename RTN> inline RTN get() = delete; template <> inline Vec3 get() { return pos; }; template <> inline BlockPos get() { return BlockPos(pos); }; template <> inline std::pair<Vec3, int> get() { return std::make_pair(pos, dimId); }; template <> inline std::pair<BlockPos, int> get() { return std::make_pair(BlockPos(pos), dimId); }; }; struct BlockPosType { BlockPos pos = BlockPos::ZERO; int dimId = 0; BlockPosType(BlockPos const& pos, int dimId = 0) : pos(pos) , dimId(dimId){}; BlockPosType(std::pair<BlockPos, int> const& pos) : pos(pos.first) , dimId(pos.second){}; template <typename RTN> inline RTN get() = delete; template <> inline BlockPos get() { return pos; }; template <> inline std::pair<BlockPos, int> get() { return std::make_pair(pos, dimId); }; template <> inline Vec3 get() { return pos.toVec3(); }; template <> inline std::pair<Vec3, int> get() { return std::make_pair(pos.toVec3(), dimId); }; }; // std::string -> json // std::string* -> bytes #define ExtraType std::nullptr_t, NumberType, Player*, Actor*, BlockActor*, Container*, WorldPosType, BlockPosType, ItemType, BlockType, NbtType #define ElementType bool, std::string, ExtraType template <typename _Ty, class... _Types> static constexpr bool is_one_of_v = std::_Meta_find_unique_index<std::variant<_Types...>, _Ty>::value < sizeof...(_Types); template <typename _Ty> static constexpr bool is_extra_type_v = std::_Is_any_of_v<_Ty, ExtraType>; static_assert(sizeof(std::variant<ElementType>) == sizeof(std::string) + 8); template <typename> constexpr bool is_vector_v = false; template <class _Ty, class _Alloc> constexpr bool is_vector_v<std::vector<_Ty, _Alloc>> = true; template <typename> constexpr bool is_map_v = false; template <class _Kty, class _Ty, class _Pr, class _Alloc> constexpr bool is_map_v<std::map<_Kty, _Ty, _Pr, _Alloc>> = true; template <class _Kty, class _Ty, class _Hasher, class _Keyeq, class _Alloc> constexpr bool is_map_v<std::unordered_map<_Kty, _Ty, _Hasher, _Keyeq, _Alloc>> = true; using Value = std::variant<ElementType>; // struct Value //{ // std::variant<ElementType> value; // Value(bool v) // : value(v){}; // Value(__int64 v) // : value(v){}; // Value(double v) // : value(v){}; // Value(std::string const& v) // : value(v){}; // Value(std::string* v) // : value(v){}; // Value(Player* v) // : value(v){}; // Value(Actor* v) // : value(v){}; // Value(ItemStack* v) // : value(v){}; // Value(Block* v) // : value(v){}; // Value(BlockActor* v) // : value(v){}; // Value(Container* v) // : value(v){}; // Value(Vec3* v) // : value(v){}; // Value(BlockPos* v) // : value(v){}; // Value(CompoundTag* v) // : value(v){}; // operator std::variant<ElementType>() // { // return value; // } // }; struct ValueType { using ArrayType = std::vector<ValueType>; using ObjectType = std::unordered_map<std::string, ValueType>; using Type = std::variant<Value, ArrayType, ObjectType>; Type value; ValueType() : value({}){}; // ValueType(ValueType const& v) = delete; // ValueType(Value const& v) = delete; ValueType(Value&& v) : value(std::move(v)){}; ValueType(Value v) : value(std::move(v)){}; // ValueType(ValueType&& v) noexcept // : value(std::move(v.value)){}; ValueType(std::vector<ValueType>&& v) : value(std::move(v)){}; ValueType(std::unordered_map<std::string, ValueType>&& v) : value(std::move(v)){}; template <typename T> ValueType(T const& v) : value(Value(v)){}; }; template <typename _Ty> static constexpr bool is_supported_type_v = std::is_void_v<_Ty> || is_one_of_v<_Ty, ElementType> || std::is_assignable_v<NumberType, _Ty> || std::is_assignable_v<NbtType, _Ty> || std::is_assignable_v<BlockType, _Ty> || std::is_assignable_v<ItemType, _Ty> || std::is_assignable_v<WorldPosType, _Ty> || std::is_assignable_v<BlockPosType, _Ty> || std::is_base_of_v<Player, std::remove_pointer_t<_Ty>> || std::is_base_of_v<Actor, std::remove_pointer_t<_Ty>>; template <typename RTN> RTN extract(ValueType&& val); template <typename T> ValueType pack(T val); template <typename RTN> RTN extractValue(Value&& value) { using Type = std::remove_const_t<std::remove_reference_t<RTN>>; static_assert(is_supported_type_v<Type>, "Unsupported Type:"); if constexpr (is_one_of_v<Type, ElementType>) return std::get<Type>(value); else if constexpr (std::is_assignable_v<NumberType, RTN>) return std::get<NumberType>(value).get<Type>(); else if constexpr (std::is_assignable_v<NbtType, RTN>) return std::get<NbtType>(value).get<Type>(); else if constexpr (std::is_assignable_v<ItemType, RTN>) return std::get<ItemType>(value).get<Type>(); else if constexpr (std::is_assignable_v<BlockType, RTN>) return std::get<BlockType>(value).get<Type>(); else if constexpr (std::is_assignable_v<WorldPosType, RTN>) return std::get<WorldPosType>(value).get<Type>(); else if constexpr (std::is_assignable_v<BlockPosType, RTN>) return std::get<BlockPosType>(value).get<Type>(); else if constexpr (std::is_base_of_v<Player, std::remove_pointer_t<RTN>>) return static_cast<RTN>(std::get<Player*>(value)); else if constexpr (std::is_base_of_v<Actor, std::remove_pointer_t<RTN>>) return static_cast<RTN>(std::get<Actor*>(value)); else if constexpr (std::is_void_v<Type>) return; else throw std::exception(fmt::format(__FUNCTION__ " - Unsupported Type: {}", typeid(RTN).name()).c_str()); } template <typename RTN> bool extractValue(std::vector<ValueType>& value, std::vector<RTN>& rtn) { for (ValueType& val : value) { rtn.emplace_back(std::move(extract<RTN>(std::move(val)))); } return true; } template <typename RTN> bool extractValue(std::unordered_map<std::string, ValueType>& value, std::unordered_map<std::string, RTN>& rtn) { for (auto& [key, val] : value) { rtn.emplace(key, std::move(extract<RTN>(std::move(val)))); } return true; } template <typename RTN> RTN extract(ValueType&& val) { if constexpr (is_vector_v<RTN>) { RTN rtn{}; extractValue(std::get<std::vector<ValueType>>(val.value), rtn); return std::move(rtn); } else if constexpr (is_map_v<RTN>) { RTN rtn{}; extractValue(std::get<std::unordered_map<std::string, ValueType>>(val.value), rtn); return std::move(rtn); } else return extractValue<RTN>(std::move(std::get<Value>(val.value))); } template <typename T> ValueType packValue(T val) { using RawType = std::remove_reference_t<std::remove_const_t<T>>; static_assert(is_supported_type_v<RawType>, "Unsupported Type"); if constexpr (is_one_of_v<RawType, ElementType>) return ValueType(std::forward<T>(val)); else if constexpr (std::is_assignable_v<NumberType, T>) return ValueType(NumberType{std::forward<T>(val)}); else if constexpr (std::is_assignable_v<NbtType, T>) return ValueType(NbtType(std::forward<T>(val))); else if constexpr (std::is_assignable_v<ItemType, T>) return ValueType(ItemType(std::forward<T>(val))); else if constexpr (std::is_assignable_v<BlockType, T>) return ValueType(BlockType(std::forward<T>(val))); else if constexpr (std::is_assignable_v<WorldPosType, T>) return ValueType(WorldPosType(std::forward<T>(val))); else if constexpr (std::is_assignable_v<BlockPosType, T>) return ValueType(BlockPosType(std::forward<T>(val))); else if constexpr (std::is_base_of_v<Player, std::remove_pointer_t<T>>) return ValueType(static_cast<Player*>(std::forward<T>(val))); else if constexpr (std::is_base_of_v<Actor, std::remove_pointer_t<T>>) return ValueType(static_cast<Actor*>(std::forward<T>(val))); else if constexpr (std::is_void_v<RawType>) return ValueType(); throw std::runtime_error(fmt::format(__FUNCTION__ " - Unsupported Type: {}", typeid(T).name()).c_str()); return ValueType(); } template <typename T> std::vector<ValueType> packArray(std::vector<T> const& val) { std::vector<ValueType> result; for (auto& v : val) { result.emplace_back(pack(v)); } return result; } template <typename T> std::unordered_map<std::string, ValueType> packObject(std::unordered_map<std::string, T> const& val) { std::unordered_map<std::string, ValueType> result; for (auto& [k, v] : val) { result.emplace(k, pack(v)); } return result; } template <typename T> ValueType pack(T val) { using RawType = std::remove_reference_t<std::remove_const_t<T>>; if constexpr (is_vector_v<RawType>) { return packArray(std::forward<T>(val)); } else if constexpr (is_map_v<RawType>) { return packObject(std::forward<T>(val)); } else return packValue(std::forward<T>(val)); } #else // Use string as value type because it is easy to convert between script types and native types using ValueType = std::string; // Json string template <typename T> std::remove_reference_t<T> extract(ValueType const& val) { return nlohmann::json::parse(val).get<std::remove_reference_t<T>>(); } template <typename T> ValueType pack(T const& val) { return nlohmann::json(val).dump(); } #endif // TEST_NEW_VALUE_TYPE using CallbackFn = std::function<ValueType(std::vector<ValueType>)>; struct ExportedFuncData { HMODULE handle; CallbackFn callback; }; LIAPI extern CallbackFn const EMPTY_FUNC; LIAPI bool exportFunc(std::string const& nameSpace, std::string const& funcName, CallbackFn&& callback, HMODULE handle = GetCurrentModule()); LIAPI CallbackFn const& importFunc(std::string const& nameSpace, std::string const& funcName); inline ValueType _expandArg(std::vector<ValueType>& args, int& index) { return std::move(args[--index]); } template <typename RTN, typename... Args> inline bool _exportAs(std::string const& nameSpace, std::string const& funcName, std::function<RTN(Args...)>&& callback) { CallbackFn cb = [callback = std::move(callback)](std::vector<ValueType> args) -> ValueType { if (sizeof...(Args) != args.size()) return std::move(ValueType()); int index = sizeof...(Args); if constexpr (std::is_void_v<RTN>) { callback(extract<Args>(_expandArg(args, index))...); return std::move(ValueType()); } else { return pack(callback(extract<Args>(_expandArg(args, index))...)); } }; return exportFunc(nameSpace, funcName, std::move(cb), GetCurrentModule()); } LIAPI bool hasFunc(std::string const& nameSpace, std::string const& funcName); LIAPI bool removeFunc(std::string const& nameSpace, std::string const& funcName); LIAPI int removeNameSpace(std::string const& nameSpace); LIAPI int removeFuncs(std::vector<std::pair<std::string, std::string>> funcs); LIAPI void _onCallError(std::string const& msg, HMODULE handle = GetCurrentModule()); template <typename RTN, typename... Args> inline bool _importAs(std::string const& nameSpace, std::string const& funcName, std::function<RTN(Args...)>& func) { func = [nameSpace, funcName](Args... args) -> RTN { auto& rawFunc = importFunc(nameSpace, funcName); if (!rawFunc) { _onCallError(fmt::format("Fail to import! Function [{}::{}] has not been exported", nameSpace, funcName)); return RTN(); } std::vector<ValueType> params = {pack(std::forward<Args>(args))...}; ValueType&& res = rawFunc(std::move(params)); return extract<RTN>(std::move(res)); }; return true; } template <typename CB, typename Func = std::conditional_t<std::is_function_v<CB>, std::function<CB>, CB>> inline Func importAs(std::string const& nameSpace, std::string const& funcName) { Func callback{}; bool res = _importAs(nameSpace, funcName, callback); return std::move(callback); } template <typename CB> inline bool exportAs(std::string const& nameSpace, std::string const& funcName, CB&& callback) { return _exportAs(nameSpace, funcName, std::function(std::move(callback))); } }; // namespace RemoteCall