diff --git a/apps/openmw-mp/Script/API/TimerAPI.cpp b/apps/openmw-mp/Script/API/TimerAPI.cpp index 7489497d9..6aac348b5 100644 --- a/apps/openmw-mp/Script/API/TimerAPI.cpp +++ b/apps/openmw-mp/Script/API/TimerAPI.cpp @@ -26,6 +26,15 @@ Timer::Timer(lua_State *lua, ScriptFuncLua callback, long msec, const std::strin } #endif +#ifdef ENABLE_MONO +Timer::Timer(MonoObject *callback, long msec, const std::string &def, std::vector<boost::any> args) : ScriptFunction(callback, 'v', def) +{ + targetMsec = msec; + this->args = args; + end = true; +} +#endif + void Timer::Tick() { if (end) @@ -93,6 +102,28 @@ int TimerAPI::CreateTimerLua(lua_State *lua, ScriptFuncLua callback, long msec, } #endif +#if defined(ENABLE_MONO) +int TimerAPI::CreateTimerMono(MonoObject *callback, long msec, const std::string& def, std::vector<boost::any> args) +{ + int id = -1; + + for (auto timer : timers) + { + if (timer.second != nullptr) + continue; + timer.second = new Timer(callback, msec, def, args); + id = timer.first; + } + + if (id == -1) + { + timers[pointer] = new Timer(callback, msec, def, args); + id = pointer; + pointer++; + } + return id; +} +#endif int TimerAPI::CreateTimer(ScriptFunc callback, long msec, const std::string &def, std::vector<boost::any> args) { diff --git a/apps/openmw-mp/Script/API/TimerAPI.hpp b/apps/openmw-mp/Script/API/TimerAPI.hpp index b20be12d6..ae324329d 100644 --- a/apps/openmw-mp/Script/API/TimerAPI.hpp +++ b/apps/openmw-mp/Script/API/TimerAPI.hpp @@ -10,6 +10,10 @@ #include <Script/Script.hpp> #include <Script/ScriptFunction.hpp> +#ifdef ENABLE_MONO +#include <mono/metadata/object.h> +#endif + namespace mwmp { @@ -24,6 +28,9 @@ namespace mwmp Timer(ScriptFunc callback, long msec, const std::string& def, std::vector<boost::any> args); #if defined(ENABLE_LUA) Timer(lua_State *lua, ScriptFuncLua callback, long msec, const std::string& def, std::vector<boost::any> args); +#endif +#ifdef ENABLE_MONO + Timer(MonoObject *callback, long msec, const std::string& def, std::vector<boost::any> args); #endif void Tick(); @@ -44,6 +51,9 @@ namespace mwmp public: #if defined(ENABLE_LUA) static int CreateTimerLua(lua_State *lua, ScriptFuncLua callback, long msec, const std::string& def, std::vector<boost::any> args); +#endif +#if defined(ENABLE_MONO) + static int CreateTimerMono(MonoObject *callback, long msec, const std::string& def, std::vector<boost::any> args); #endif static int CreateTimer(ScriptFunc callback, long msec, const std::string& def, std::vector<boost::any> args); static void FreeTimer(int timerid); diff --git a/apps/openmw-mp/Script/LangMono/LangMono.cpp b/apps/openmw-mp/Script/LangMono/LangMono.cpp index 1a4a107f7..65c37f514 100644 --- a/apps/openmw-mp/Script/LangMono/LangMono.cpp +++ b/apps/openmw-mp/Script/LangMono/LangMono.cpp @@ -4,13 +4,107 @@ #include <cstdarg> #include <mono/metadata/appdomain.h> +#include <mono/metadata/object-forward.h> #include <mono/jit/jit.h> #include <mono/metadata/assembly.h> +#include <mono/metadata/mono-config.h> #include <apps/openmw-mp/Script/ScriptFunctions.hpp> +#include <apps/openmw-mp/Script/API/TimerAPI.hpp> #include "LangMono.hpp" static MonoDomain *domain = nullptr; // shared domain +std::string monoStringToStdString(MonoString *monoString) +{ + char *utf8 = mono_string_to_utf8(monoString); + std::string str = utf8; + mono_free(utf8); + return str; +} + +template<typename T> +T unbox(MonoObject *obj) +{ + return *(T *) mono_object_unbox(obj); +} + +boost::any monoObjectToAny(MonoObject *obj) +{ + + MonoClass *klass = mono_object_get_class(obj); + MonoType *rawType = mono_class_get_type(klass); + + switch ((MonoTypeEnum) mono_type_get_type(rawType)) + { + case MONO_TYPE_END: + case MONO_TYPE_VOID: + break; + case MONO_TYPE_BOOLEAN: + return (bool) unbox<MonoBoolean>(obj); + case MONO_TYPE_CHAR: + return unbox<uint16_t>(obj); + case MONO_TYPE_I1: + return unbox<int8_t>(obj); + case MONO_TYPE_U1: + return unbox<uint8_t>(obj); + case MONO_TYPE_I2: + return unbox<int16_t>(obj); + case MONO_TYPE_U2: + return unbox<uint16_t>(obj); + case MONO_TYPE_I4: + return unbox<int32_t>(obj); + case MONO_TYPE_U4: + return unbox<uint32_t>(obj); + case MONO_TYPE_I8: + return unbox<int64_t>(obj); + case MONO_TYPE_U8: + return unbox<uint64_t>(obj); + case MONO_TYPE_R4: + return unbox<float>(obj); + case MONO_TYPE_R8: + return unbox<double>(obj); + case MONO_TYPE_STRING: + return monoStringToStdString((MonoString *) obj); + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + { + MonoArrayType *arrayType = mono_type_get_array_type(rawType); + } + default: + return boost::any(); + } + return boost::any(); +} + +int LangMono::CreateTimerEx(MonoObject *delegate, long msec, MonoString *monoStr, MonoArray *monoArgs) +{ + size_t argsLength = mono_array_length(monoArgs); + std::vector<boost::any> params (argsLength); + try + { + for(size_t i = 0; i < argsLength; ++i) + params[i] = monoObjectToAny(mono_array_get(monoArgs, MonoObject*, i)); + + char *types = mono_string_to_utf8(monoStr); + int id = mwmp::TimerAPI::CreateTimerMono(delegate, msec, types, params); + mono_free(types); + return id; + } + catch (...) + { + return -1; + } +} + +void LangMono::MakePublic(MonoObject *delegate, const char *name) noexcept +{ + +} + +MonoObject *LangMono::CallPublic(const char *name, MonoArray *args) +{ + return nullptr; +} lib_t LangMono::GetInterface() { @@ -40,6 +134,9 @@ void LangMono::Init() return; domain = mono_jit_init("TES3MP Mono VM"); // will leak :P + mono_add_internal_call("TES3MPSharp.TES3MP::CreateTimerEx", (void*) &LangMono::CreateTimerEx); + mono_add_internal_call("TES3MPSharp.TES3MP::MakePublic", (void*) &LangMono::MakePublic); + mono_add_internal_call("TES3MPSharp.TES3MP::CallPublic", (void*) &LangMono::CallPublic); } std::vector<MonoClass *> getInstanceClassList(MonoImage *image, const std::string &parentName) @@ -109,8 +206,7 @@ boost::any LangMono::Call(const char *name, const char *argl, int buf, ...) int n_args = (int) (strlen(argl)); - std::vector<void *> vec; - vec.reserve(n_args); + std::vector<void *> vec(n_args); for (int index = 0; index < n_args; index++) { @@ -119,48 +215,48 @@ boost::any LangMono::Call(const char *name, const char *argl, int buf, ...) case 'i': { auto val = va_arg(vargs, unsigned int); - vec.push_back((void *) &val); + vec[index] = (void *) &val; break; } case 'q': { auto val = va_arg(vargs, signed int); - vec.push_back((void *) &val); + vec[index] = (void *) &val; break; } case 'l': { auto val = va_arg(vargs, unsigned long long); - vec.push_back((void *) &val); + vec[index] = (void *) &val; break; } case 'w': { auto val = va_arg(vargs, signed long long); - vec.push_back((void *) &val); + vec[index] = (void *) &val; break; } case 'f': { auto val = va_arg(vargs, double); - vec.push_back((void *) &val); + vec[index] = (void *) &val; break; } case 'p': { auto val = va_arg(vargs, void*); - vec.push_back((void *) &val); + vec[index] = (void *) &val; break; } case 's': { - vec.push_back(mono_string_new(mono_domain_get(), va_arg(vargs, const char*))); + vec[index] = (mono_string_new(mono_domain_get(), va_arg(vargs, const char*))); break; } case 'b': { auto val = va_arg(vargs, int); - vec.push_back((void *) &val); + vec[index] = (void *) &val; break; } @@ -182,6 +278,9 @@ boost::any LangMono::Call(const char *name, const char *argl, int buf, ...) methodsCache[{name, n_args}] = method; } + if (method == nullptr) + return boost::any(); + MonoObject *ret = mono_runtime_invoke(method, instance->object, vec.data(), nullptr); if (ret) return boost::any(mono_object_unbox(ret)); diff --git a/apps/openmw-mp/Script/LangMono/LangMono.hpp b/apps/openmw-mp/Script/LangMono/LangMono.hpp index df446af9c..00269d228 100644 --- a/apps/openmw-mp/Script/LangMono/LangMono.hpp +++ b/apps/openmw-mp/Script/LangMono/LangMono.hpp @@ -63,6 +63,10 @@ public: virtual boost::any Call(const char *name, const char *argl, int buf, ...) override; virtual boost::any Call(const char *name, const char *argl, const std::vector<boost::any> &args) override; + static int CreateTimerEx(MonoObject *delegate, long msec, MonoString *monoStr, MonoArray *args); + static void MakePublic(MonoObject *delegate, const char *name) noexcept; + static MonoObject *CallPublic(const char *name, MonoArray *args); + private: void Init(); }; diff --git a/apps/openmw-mp/Script/ScriptFunction.cpp b/apps/openmw-mp/Script/ScriptFunction.cpp index b176776f9..b2483dfd3 100644 --- a/apps/openmw-mp/Script/ScriptFunction.cpp +++ b/apps/openmw-mp/Script/ScriptFunction.cpp @@ -24,7 +24,12 @@ ScriptFunction::ScriptFunction(const ScriptFuncLua &fLua, lua_State *lua, char r } #endif - +#if defined (ENABLE_MONO) +ScriptFunction::ScriptFunction(MonoObject *delegate, char ret_type, const std::string &def) : + fMono({delegate}), ret_type(ret_type), def(def), script_type(SCRIPT_MONO) +{ +} +#endif ScriptFunction::~ScriptFunction() { @@ -36,7 +41,7 @@ ScriptFunction::~ScriptFunction() boost::any ScriptFunction::Call(const vector<boost::any> &args) { - boost::any result; + boost::any result = boost::any(); if (def.length() != args.size()) throw runtime_error("Script call: Number of arguments does not match definition"); @@ -61,13 +66,84 @@ boost::any ScriptFunction::Call(const vector<boost::any> &args) result = boost::any_cast<luabridge::LuaRef>(any).cast<const char*>(); break; case 'v': - result = boost::any(); break; default: throw runtime_error("Lua call: Unknown return type" + ret_type); } } #endif +#if defined (ENABLE_MONO) + else if (script_type == SCRIPT_MONO) + { + std::vector<void*> argList; + argList.resize(args.size()); + + for (int index = 0; index < args.size(); index++) + { + switch (def[index]) + { + case 'i': + { + auto val = boost::any_cast<unsigned int>(args.at(index)); + argList[index] = ((void *) &val); + break; + } + case 'q': + { + auto val = boost::any_cast<signed int>(args.at(index)); + argList[index] = ((void *) &val); + break; + } + case 'l': + { + auto val = boost::any_cast<unsigned long long>(args.at(index)); + argList[index] = ((void *) &val); + break; + } + case 'w': + { + auto val = boost::any_cast<signed long long>(args.at(index)); + argList[index] = ((void *) &val); + break; + } + case 'f': + { + auto val = boost::any_cast<double>(args.at(index)); + argList[index] = ((void *) &val); + break; + } + case 'p': + { + auto val = boost::any_cast<void *>(args.at(index)); + argList[index] = ((void *) &val); + break; + } + case 's': + { + if (args.at(index).type() == typeid(std::string)) // mono to mono call + argList[index] = mono_string_new(mono_domain_get(), boost::any_cast<std::string>(args.at(index)).c_str()); + else // lua to mono + argList[index] = mono_string_new(mono_domain_get(), boost::any_cast<const char *>(args.at(index))); + break; + } + case 'b': + { + auto val = boost::any_cast<int>(args.at(index)); + argList[index] = ((void *) &val); + break; + } + + default: + throw std::runtime_error("Call: Unknown argument identifier " + def[index]); + } + } + + MonoObject *monoRet = mono_runtime_delegate_invoke(fMono.delegate, argList.data(), NULL); + if (monoRet != nullptr) + result = mono_object_unbox(monoRet); // todo cast + + } +#endif return result; } diff --git a/apps/openmw-mp/Script/ScriptFunction.hpp b/apps/openmw-mp/Script/ScriptFunction.hpp index 6659d8098..e58e2cbcd 100644 --- a/apps/openmw-mp/Script/ScriptFunction.hpp +++ b/apps/openmw-mp/Script/ScriptFunction.hpp @@ -12,6 +12,11 @@ #include "LangLua/LangLua.hpp" #endif +#if defined (ENABLE_MONO) +#include <mono/metadata/object.h> +#include <mono/metadata/appdomain.h> +#endif + typedef unsigned long long(*ScriptFunc)(); #if defined (ENABLE_LUA) typedef std::string ScriptFuncLua; @@ -29,6 +34,12 @@ protected: lua_State *lua; ScriptFuncLua name; } fLua; +#endif +#ifdef ENABLE_MONO + struct + { + MonoObject *delegate; + } fMono; #endif }; @@ -39,12 +50,16 @@ protected: enum { SCRIPT_CPP, - SCRIPT_LUA + SCRIPT_LUA, + SCRIPT_MONO }; ScriptFunction(ScriptFunc fCpp, char ret_type, const std::string &def); #if defined (ENABLE_LUA) ScriptFunction(const ScriptFuncLua &fPawn, lua_State *lua, char ret_type, const std::string &def); +#endif +#if defined (ENABLE_MONO) + ScriptFunction(MonoObject *delegate, char ret_type, const std::string &def); #endif virtual ~ScriptFunction();