#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("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 tag) : ptr(tag.release()) , own(true){}; NbtType(CompoundTag const* ptr) : ptr(ptr) , own(false){}; inline std::unique_ptr tryGetUniquePtr() { if (!own) return {}; own = false; auto uptr = std::unique_ptr(const_cast(ptr)); ptr = nullptr; return std::move(uptr); } template inline RTN get() = delete; template <> inline CompoundTag const* get() { return ptr; }; template <> inline CompoundTag* get() { return const_cast(ptr); }; template <> inline std::unique_ptr get() { return tryGetUniquePtr(); }; }; struct ItemType { ItemStack const* ptr = nullptr; bool own = false; ItemType(std::unique_ptr tag) : ptr(tag.release()) , own(true){}; ItemType(ItemStack const* ptr) : ptr(ptr) , own(false){}; inline std::unique_ptr tryGetUniquePtr() { if (!own) return {}; own = false; auto uptr = std::unique_ptr(const_cast(ptr)); ptr = nullptr; return std::move(uptr); } template inline RTN get() = delete; template <> inline ItemStack const* get() { return ptr; }; template <> inline ItemStack* get() { return const_cast(ptr); }; template <> inline std::unique_ptr get() { return tryGetUniquePtr(); }; }; struct BlockType { BlockInstance instance; BlockType(BlockInstance instance) : instance(instance){}; BlockType(Block const* ptr) : instance(BlockInstance::createBlockInstance(const_cast(ptr), BlockPos::ZERO, -1)){}; template 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 std::enable_if_t || std::is_floating_point_v, NumberType&> operator=(T v) { i = static_cast<__int64>(v); f = static_cast(v); } NumberType(double v) : i(static_cast<__int64>(v)) , f(static_cast(v)){}; NumberType(float v) : i(static_cast<__int64>(v)) , f(static_cast(v)){}; NumberType(__int64 v) : i(static_cast<__int64>(v)) , f(static_cast(v)){}; NumberType(int v) : i(static_cast<__int64>(v)) , f(static_cast(v)){}; NumberType(short v) : i(static_cast<__int64>(v)) , f(static_cast(v)){}; NumberType(char v) : i(static_cast<__int64>(v)) , f(static_cast(v)){}; NumberType(unsigned __int64 v) : i(static_cast<__int64>(v)) , f(static_cast(v)){}; NumberType(unsigned int v) : i(static_cast<__int64>(v)) , f(static_cast(v)){}; NumberType(unsigned short v) : i(static_cast<__int64>(v)) , f(static_cast(v)){}; NumberType(unsigned char v) : i(static_cast<__int64>(v)) , f(static_cast(v)){}; template inline std::enable_if_t, RTN> get() { return static_cast(i); }; template inline std::enable_if_t, RTN> get() { return static_cast(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 const& pos) : pos(pos.first) , dimId(pos.second){}; template inline RTN get() = delete; template <> inline Vec3 get() { return pos; }; template <> inline BlockPos get() { return BlockPos(pos); }; template <> inline std::pair get() { return std::make_pair(pos, dimId); }; template <> inline std::pair 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 const& pos) : pos(pos.first) , dimId(pos.second){}; template inline RTN get() = delete; template <> inline BlockPos get() { return pos; }; template <> inline std::pair get() { return std::make_pair(pos, dimId); }; template <> inline Vec3 get() { return pos.toVec3(); }; template <> inline std::pair 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 static constexpr bool is_one_of_v = std::_Meta_find_unique_index, _Ty>::value < sizeof...(_Types); template static constexpr bool is_extra_type_v = std::_Is_any_of_v<_Ty, ExtraType>; static_assert(sizeof(std::variant) == sizeof(std::string) + 8); template constexpr bool is_vector_v = false; template constexpr bool is_vector_v> = true; template constexpr bool is_map_v = false; template constexpr bool is_map_v> = true; template constexpr bool is_map_v> = true; using Value = std::variant; // struct Value //{ // std::variant 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() // { // return value; // } // }; struct ValueType { using ArrayType = std::vector; using ObjectType = std::unordered_map; using Type = std::variant; 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&& v) : value(std::move(v)){}; ValueType(std::unordered_map&& v) : value(std::move(v)){}; template ValueType(T const& v) : value(Value(v)){}; }; template static constexpr bool is_supported_type_v = std::is_void_v<_Ty> || is_one_of_v<_Ty, ElementType> || std::is_assignable_v || std::is_assignable_v || std::is_assignable_v || std::is_assignable_v || std::is_assignable_v || std::is_assignable_v || std::is_base_of_v> || std::is_base_of_v>; template RTN extract(ValueType&& val); template ValueType pack(T val); template RTN extractValue(Value&& value) { using Type = std::remove_const_t>; static_assert(is_supported_type_v, "Unsupported Type:"); if constexpr (is_one_of_v) return std::get(value); else if constexpr (std::is_assignable_v) return std::get(value).get(); else if constexpr (std::is_assignable_v) return std::get(value).get(); else if constexpr (std::is_assignable_v) return std::get(value).get(); else if constexpr (std::is_assignable_v) return std::get(value).get(); else if constexpr (std::is_assignable_v) return std::get(value).get(); else if constexpr (std::is_assignable_v) return std::get(value).get(); else if constexpr (std::is_base_of_v>) return static_cast(std::get(value)); else if constexpr (std::is_base_of_v>) return static_cast(std::get(value)); else if constexpr (std::is_void_v) return; else throw std::exception(fmt::format(__FUNCTION__ " - Unsupported Type: {}", typeid(RTN).name()).c_str()); } template bool extractValue(std::vector& value, std::vector& rtn) { for (ValueType& val : value) { rtn.emplace_back(std::move(extract(std::move(val)))); } return true; } template bool extractValue(std::unordered_map& value, std::unordered_map& rtn) { for (auto& [key, val] : value) { rtn.emplace(key, std::move(extract(std::move(val)))); } return true; } template RTN extract(ValueType&& val) { if constexpr (is_vector_v) { RTN rtn{}; extractValue(std::get>(val.value), rtn); return std::move(rtn); } else if constexpr (is_map_v) { RTN rtn{}; extractValue(std::get>(val.value), rtn); return std::move(rtn); } else return extractValue(std::move(std::get(val.value))); } template ValueType packValue(T val) { using RawType = std::remove_reference_t>; static_assert(is_supported_type_v, "Unsupported Type"); if constexpr (is_one_of_v) return ValueType(std::forward(val)); else if constexpr (std::is_assignable_v) return ValueType(NumberType{std::forward(val)}); else if constexpr (std::is_assignable_v) return ValueType(NbtType(std::forward(val))); else if constexpr (std::is_assignable_v) return ValueType(ItemType(std::forward(val))); else if constexpr (std::is_assignable_v) return ValueType(BlockType(std::forward(val))); else if constexpr (std::is_assignable_v) return ValueType(WorldPosType(std::forward(val))); else if constexpr (std::is_assignable_v) return ValueType(BlockPosType(std::forward(val))); else if constexpr (std::is_base_of_v>) return ValueType(static_cast(std::forward(val))); else if constexpr (std::is_base_of_v>) return ValueType(static_cast(std::forward(val))); else if constexpr (std::is_void_v) return ValueType(); throw std::runtime_error(fmt::format(__FUNCTION__ " - Unsupported Type: {}", typeid(T).name()).c_str()); return ValueType(); } template std::vector packArray(std::vector const& val) { std::vector result; for (auto& v : val) { result.emplace_back(pack(v)); } return result; } template std::unordered_map packObject(std::unordered_map const& val) { std::unordered_map result; for (auto& [k, v] : val) { result.emplace(k, pack(v)); } return result; } template ValueType pack(T val) { using RawType = std::remove_reference_t>; if constexpr (is_vector_v) { return packArray(std::forward(val)); } else if constexpr (is_map_v) { return packObject(std::forward(val)); } else return packValue(std::forward(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 std::remove_reference_t extract(ValueType const& val) { return nlohmann::json::parse(val).get>(); } template ValueType pack(T const& val) { return nlohmann::json(val).dump(); } #endif // TEST_NEW_VALUE_TYPE using CallbackFn = std::function)>; 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& args, int& index) { return std::move(args[--index]); } template inline bool _exportAs(std::string const& nameSpace, std::string const& funcName, std::function&& callback) { CallbackFn cb = [callback = std::move(callback)](std::vector args) -> ValueType { if (sizeof...(Args) != args.size()) return std::move(ValueType()); int index = sizeof...(Args); if constexpr (std::is_void_v) { callback(extract(_expandArg(args, index))...); return std::move(ValueType()); } else { return pack(callback(extract(_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> funcs); LIAPI void _onCallError(std::string const& msg, HMODULE handle = GetCurrentModule()); template inline bool _importAs(std::string const& nameSpace, std::string const& funcName, std::function& 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 params = {pack(std::forward(args))...}; ValueType&& res = rawFunc(std::move(params)); return extract(std::move(res)); }; return true; } template , std::function, 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 inline bool exportAs(std::string const& nameSpace, std::string const& funcName, CB&& callback) { return _exportAs(nameSpace, funcName, std::function(std::move(callback))); } }; // namespace RemoteCall