forked from mirror/openmw-tes3mp
[Server] Init mono branch
This commit is contained in:
parent
4ce0331f1b
commit
5840c7bba2
8 changed files with 376 additions and 7 deletions
|
@ -32,6 +32,20 @@ if(BUILD_WITH_LUA)
|
|||
include_directories(SYSTEM ${LuaJit_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/extern/LuaBridge)
|
||||
endif(BUILD_WITH_LUA)
|
||||
|
||||
option(BUILD_WITH_MONO "Enable Mono languages" ON)
|
||||
if(BUILD_WITH_MONO)
|
||||
message(STATUS "Mono enabled")
|
||||
find_package(Mono REQUIRED)
|
||||
include_directories(${MONO_INCLUDE_DIR})
|
||||
|
||||
set(MonoScript_Sources
|
||||
Script/LangMono/LangMono.cpp
|
||||
)
|
||||
set(MonoScript_Headers
|
||||
Script/LangMono/LangMono.hpp
|
||||
)
|
||||
endif(BUILD_WITH_MONO)
|
||||
|
||||
set(NativeScript_Sources
|
||||
Script/LangNative/LangNative.cpp
|
||||
)
|
||||
|
@ -64,7 +78,7 @@ set(SERVER
|
|||
Script/API/TimerAPI.cpp Script/API/PublicFnAPI.cpp
|
||||
${LuaScript_Sources}
|
||||
${NativeScript_Sources}
|
||||
|
||||
${MonoScript_Sources}
|
||||
)
|
||||
|
||||
set(SERVER_HEADER
|
||||
|
@ -73,6 +87,7 @@ set(SERVER_HEADER
|
|||
Script/ScriptFunctions.hpp Script/API/TimerAPI.hpp Script/API/PublicFnAPI.hpp
|
||||
${LuaScript_Headers}
|
||||
${NativeScript_Headers}
|
||||
${MonoScript_Headers}
|
||||
)
|
||||
source_group(tes3mp-server FILES ${SERVER} ${SERVER_HEADER})
|
||||
|
||||
|
@ -177,6 +192,7 @@ target_link_libraries(tes3mp-server
|
|||
${RakNet_LIBRARY}
|
||||
components
|
||||
${LuaJit_LIBRARIES}
|
||||
${MONO_LIBRARY}
|
||||
${Breakpad_Library}
|
||||
)
|
||||
|
||||
|
|
268
apps/openmw-mp/Script/LangMono/LangMono.cpp
Normal file
268
apps/openmw-mp/Script/LangMono/LangMono.cpp
Normal file
|
@ -0,0 +1,268 @@
|
|||
//
|
||||
// Created by koncord on 08.12.18.
|
||||
//
|
||||
|
||||
#include <cstdarg>
|
||||
#include <mono/metadata/appdomain.h>
|
||||
#include <mono/jit/jit.h>
|
||||
#include <mono/metadata/assembly.h>
|
||||
#include <apps/openmw-mp/Script/ScriptFunctions.hpp>
|
||||
#include "LangMono.hpp"
|
||||
|
||||
static MonoDomain *domain = nullptr; // shared domain
|
||||
|
||||
|
||||
lib_t LangMono::GetInterface()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LangMono::LangMono()
|
||||
{
|
||||
instance = new MonoInstance;
|
||||
|
||||
Init();
|
||||
}
|
||||
|
||||
LangMono::LangMono(MonoInstance *instance) : instance(instance)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
LangMono::~LangMono()
|
||||
{
|
||||
delete instance;
|
||||
}
|
||||
|
||||
void LangMono::Init()
|
||||
{
|
||||
if (domain != nullptr)
|
||||
return;
|
||||
|
||||
domain = mono_jit_init("TES3MP Mono VM"); // will leak :P
|
||||
}
|
||||
|
||||
std::vector<MonoClass *> getInstanceClassList(MonoImage *image, const std::string &parentName)
|
||||
{
|
||||
std::vector<MonoClass *> classes;
|
||||
|
||||
const MonoTableInfo *tableInfo = mono_image_get_table_info(image, MONO_TABLE_TYPEDEF);
|
||||
|
||||
int rows = mono_table_info_get_rows(tableInfo);
|
||||
|
||||
for (int i = 0; i < rows; i++)
|
||||
{
|
||||
MonoClass *klass = nullptr;
|
||||
uint32_t cols[MONO_TYPEDEF_SIZE];
|
||||
mono_metadata_decode_row(tableInfo, i, cols, MONO_TYPEDEF_SIZE);
|
||||
const char *name = mono_metadata_string_heap(image, cols[MONO_TYPEDEF_NAME]);
|
||||
const char *nameSpace = mono_metadata_string_heap(image, cols[MONO_TYPEDEF_NAMESPACE]);
|
||||
klass = mono_class_from_name(image, nameSpace, name);
|
||||
|
||||
if (klass == nullptr)
|
||||
continue;
|
||||
|
||||
MonoClass *parent = mono_class_get_parent(klass);
|
||||
if (parent != nullptr && parentName == mono_class_get_name(parent))
|
||||
classes.push_back(klass);
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
void LangMono::LoadProgram(const char *filename)
|
||||
{
|
||||
instance->assembly = mono_domain_assembly_open(domain, filename);
|
||||
instance->image = mono_assembly_get_image(instance->assembly);
|
||||
|
||||
std::vector<MonoClass *> list = getInstanceClassList(instance->image, "Instance");
|
||||
for (auto &&klass : list)
|
||||
{
|
||||
instance->object = mono_object_new(domain, klass);
|
||||
mono_runtime_object_init(instance->object); // call default ctor
|
||||
|
||||
instance->klass = klass;
|
||||
}
|
||||
}
|
||||
|
||||
int LangMono::FreeProgram()
|
||||
{
|
||||
for(auto &&method : methodsCache)
|
||||
mono_free_method(method.second);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool LangMono::IsCallbackPresent(const char *name)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
boost::any LangMono::Call(const char *name, const char *argl, int buf, ...)
|
||||
{
|
||||
va_list vargs;
|
||||
va_start(vargs, buf);
|
||||
|
||||
int n_args = (int) (strlen(argl));
|
||||
|
||||
std::vector<void *> vec;
|
||||
vec.reserve(n_args);
|
||||
|
||||
for (int index = 0; index < n_args; index++)
|
||||
{
|
||||
switch (argl[index])
|
||||
{
|
||||
case 'i':
|
||||
{
|
||||
auto val = va_arg(vargs, unsigned int);
|
||||
vec.push_back((void *) &val);
|
||||
break;
|
||||
}
|
||||
case 'q':
|
||||
{
|
||||
auto val = va_arg(vargs, signed int);
|
||||
vec.push_back((void *) &val);
|
||||
break;
|
||||
}
|
||||
case 'l':
|
||||
{
|
||||
auto val = va_arg(vargs, unsigned long long);
|
||||
vec.push_back((void *) &val);
|
||||
break;
|
||||
}
|
||||
case 'w':
|
||||
{
|
||||
auto val = va_arg(vargs, signed long long);
|
||||
vec.push_back((void *) &val);
|
||||
break;
|
||||
}
|
||||
case 'f':
|
||||
{
|
||||
auto val = va_arg(vargs, double);
|
||||
vec.push_back((void *) &val);
|
||||
break;
|
||||
}
|
||||
case 'p':
|
||||
{
|
||||
auto val = va_arg(vargs, void*);
|
||||
vec.push_back((void *) &val);
|
||||
break;
|
||||
}
|
||||
case 's':
|
||||
{
|
||||
vec.push_back(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);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw std::runtime_error("Mono call: Unknown argument identifier " + argl[index]);
|
||||
}
|
||||
}
|
||||
|
||||
va_end(vargs);
|
||||
|
||||
MonoMethod *method;
|
||||
auto it = methodsCache.find({name, n_args});
|
||||
if (it != methodsCache.end())
|
||||
{
|
||||
method = it->second;
|
||||
} else
|
||||
{
|
||||
method = mono_class_get_method_from_name(instance->klass, name, n_args);
|
||||
methodsCache[{name, n_args}] = method;
|
||||
}
|
||||
|
||||
MonoObject *ret = mono_runtime_invoke(method, instance->object, vec.data(), nullptr);
|
||||
if (ret)
|
||||
return boost::any(mono_object_unbox(ret));
|
||||
else
|
||||
return boost::any();
|
||||
}
|
||||
|
||||
boost::any LangMono::Call(const char *name, const char *argl, const std::vector<boost::any> &args)
|
||||
{
|
||||
int n_args = args.size();
|
||||
|
||||
std::vector<void *> vec(n_args);
|
||||
|
||||
for (int index = 0; index < n_args; index++)
|
||||
{
|
||||
switch (argl[index])
|
||||
{
|
||||
case 'i':
|
||||
{
|
||||
auto val = boost::any_cast<unsigned int>(args.at(index));
|
||||
vec[index] = ((void *) &val);
|
||||
break;
|
||||
}
|
||||
case 'q':
|
||||
{
|
||||
auto val = boost::any_cast<signed int>(args.at(index));
|
||||
vec[index] = ((void *) &val);
|
||||
break;
|
||||
}
|
||||
case 'l':
|
||||
{
|
||||
auto val = boost::any_cast<unsigned long long>(args.at(index));
|
||||
vec[index] = ((void *) &val);
|
||||
break;
|
||||
}
|
||||
case 'w':
|
||||
{
|
||||
auto val = boost::any_cast<signed long long>(args.at(index));
|
||||
vec[index] = ((void *) &val);
|
||||
break;
|
||||
}
|
||||
case 'f':
|
||||
{
|
||||
auto val = boost::any_cast<double>(args.at(index));
|
||||
vec[index] = ((void *) &val);
|
||||
break;
|
||||
}
|
||||
case 'p':
|
||||
{
|
||||
auto val = boost::any_cast<void *>(args.at(index));
|
||||
vec[index] = ((void *) &val);
|
||||
break;
|
||||
}
|
||||
case 's':
|
||||
{
|
||||
auto val = mono_string_new(mono_domain_get(), boost::any_cast<const char *>(args.at(index)));
|
||||
vec[index] = (val);
|
||||
break;
|
||||
}
|
||||
case 'b':
|
||||
{
|
||||
auto val = boost::any_cast<int>(args.at(index));
|
||||
vec[index] = ((void *) &val);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw std::runtime_error("Mono call: Unknown argument identifier " + argl[index]);
|
||||
}
|
||||
}
|
||||
|
||||
MonoMethod *method;
|
||||
auto it = methodsCache.find({name, n_args});
|
||||
if (it != methodsCache.end())
|
||||
{
|
||||
method = it->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
method = mono_class_get_method_from_name(instance->klass, name, n_args);
|
||||
methodsCache[{name, n_args}] = method;
|
||||
}
|
||||
|
||||
MonoObject *ret = mono_runtime_invoke(method, instance->object, vec.data(), nullptr);
|
||||
if (ret)
|
||||
return boost::any(mono_object_unbox(ret));
|
||||
else
|
||||
return boost::any();
|
||||
}
|
68
apps/openmw-mp/Script/LangMono/LangMono.hpp
Normal file
68
apps/openmw-mp/Script/LangMono/LangMono.hpp
Normal file
|
@ -0,0 +1,68 @@
|
|||
//
|
||||
// Created by koncord on 08.12.18.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Script/Language.hpp>
|
||||
#include <Script/SystemInterface.hpp>
|
||||
#include <unordered_map>
|
||||
#include <mono/metadata/object.h>
|
||||
|
||||
struct MethodKey
|
||||
{
|
||||
inline bool operator==(const MethodKey &other) const
|
||||
{
|
||||
return other.paramsCnt == paramsCnt && other.name == name;
|
||||
}
|
||||
|
||||
MethodKey(const std::string &name, int paramsCnt)
|
||||
{
|
||||
this->name = name;
|
||||
this->paramsCnt = paramsCnt;
|
||||
}
|
||||
|
||||
std::string name;
|
||||
int paramsCnt;
|
||||
};
|
||||
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<>
|
||||
struct hash<MethodKey>
|
||||
{
|
||||
std::size_t operator()(const MethodKey &key) const
|
||||
{
|
||||
return hash<string>()(key.name)
|
||||
^ (hash<uint32_t>()(key.paramsCnt) >> 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
struct MonoInstance
|
||||
{
|
||||
MonoObject *object;
|
||||
MonoClass *klass;
|
||||
MonoAssembly *assembly;
|
||||
MonoImage *image;
|
||||
};
|
||||
|
||||
class LangMono: public Language
|
||||
{
|
||||
MonoInstance *instance;
|
||||
std::unordered_map<MethodKey, MonoMethod *> methodsCache;
|
||||
public:
|
||||
virtual lib_t GetInterface() override;
|
||||
LangMono();
|
||||
LangMono(MonoInstance *instance);
|
||||
~LangMono();
|
||||
virtual void LoadProgram(const char *filename) override;
|
||||
virtual int FreeProgram() override;
|
||||
virtual bool IsCallbackPresent(const char *name) override;
|
||||
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;
|
||||
|
||||
private:
|
||||
void Init();
|
||||
};
|
|
@ -13,7 +13,7 @@
|
|||
class Language
|
||||
{
|
||||
public:
|
||||
virtual ~Language(){}
|
||||
virtual ~Language() = default;
|
||||
virtual void LoadProgram(const char* filename) = 0;
|
||||
virtual int FreeProgram() = 0;
|
||||
virtual bool IsCallbackPresent(const char* name) = 0;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Created by koncord on 19.03.16.
|
||||
//
|
||||
|
||||
#include <apps/openmw-mp/Script/LangMono/LangMono.hpp>
|
||||
#include "Script.hpp"
|
||||
#include "LangNative/LangNative.hpp"
|
||||
|
||||
|
@ -23,6 +24,12 @@ Script::Script(const char *path)
|
|||
|
||||
fclose(file);
|
||||
|
||||
if (strstr(path, ".dll"))
|
||||
{
|
||||
script_type = SCRIPT_MONO;
|
||||
lang = new LangMono();
|
||||
}
|
||||
/*else
|
||||
#ifdef _WIN32
|
||||
if (strstr(path, ".dll"))
|
||||
#else
|
||||
|
@ -31,7 +38,7 @@ Script::Script(const char *path)
|
|||
{
|
||||
script_type = SCRIPT_CPP;
|
||||
lang = new LangNative();
|
||||
}
|
||||
}*/
|
||||
#if defined (ENABLE_LUA)
|
||||
else if (strstr(path, ".lua") || strstr(path, ".t"))
|
||||
{
|
||||
|
|
|
@ -27,7 +27,8 @@ private:
|
|||
enum
|
||||
{
|
||||
SCRIPT_CPP,
|
||||
SCRIPT_LUA
|
||||
SCRIPT_LUA,
|
||||
SCRIPT_MONO
|
||||
};
|
||||
|
||||
template<typename R>
|
||||
|
@ -95,8 +96,7 @@ public:
|
|||
|
||||
if (script->script_type == SCRIPT_CPP)
|
||||
(callback)(std::forward<Args>(args)...);
|
||||
#if defined (ENABLE_LUA)
|
||||
else if (script->script_type == SCRIPT_LUA)
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -110,7 +110,6 @@ public:
|
|||
throw;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
++count;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "Utils.hpp"
|
||||
|
||||
#include <apps/openmw-mp/Script/Script.hpp>
|
||||
#include <mono/metadata/assembly.h>
|
||||
|
||||
#ifdef ENABLE_BREAKPAD
|
||||
#include <handler/exception_handler.h>
|
||||
|
@ -234,6 +235,7 @@ int main(int argc, char *argv[])
|
|||
#endif
|
||||
|
||||
#endif
|
||||
mono_set_dirs(Utils::convertPath(plugin_home + "/mono").c_str(), Utils::convertPath(plugin_home + "/mono/etc").c_str());
|
||||
|
||||
int code;
|
||||
|
||||
|
|
9
cmake/FindMono.cmake
Normal file
9
cmake/FindMono.cmake
Normal file
|
@ -0,0 +1,9 @@
|
|||
include(LibFindMacros)
|
||||
|
||||
libfind_pkg_detect(MONO mono-2.0
|
||||
FIND_PATH mono/jit/jit.h
|
||||
PATH_SUFFIXES mono-2.0
|
||||
FIND_LIBRARY NAMES mono-2.0
|
||||
)
|
||||
|
||||
libfind_process(MONO)
|
Loading…
Reference in a new issue