Add experimental plugin system
Plugins define the interface specified in plugin.h and have full access to any functions exposed in mkxp. Specify plugins to be loaded in the config file via 'plugin=/path/to/plugin.so' lines. A sample plugin for the MRI binding is provided.
This commit is contained in:
parent
3daf805350
commit
94c2f2b60e
|
@ -26,6 +26,7 @@
|
|||
#include "filesystem.h"
|
||||
#include "util.h"
|
||||
#include "debugwriter.h"
|
||||
#include "plugin.h"
|
||||
|
||||
#include <ruby.h>
|
||||
#include <ruby/encoding.h>
|
||||
|
@ -290,6 +291,21 @@ static void mriBindingExecute()
|
|||
|
||||
mriBindingInit();
|
||||
|
||||
PluginLoader *pluginLoader;
|
||||
|
||||
try
|
||||
{
|
||||
pluginLoader = new PluginLoader(PLUGIN_MRI);
|
||||
}
|
||||
catch (const Exception &exc)
|
||||
{
|
||||
showMsg(exc.msg);
|
||||
ruby_cleanup(0);
|
||||
shState->rtData().rqTermAck = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
std::string &customScript = shState->rtData().config.customScript;
|
||||
if (!customScript.empty())
|
||||
runCustomScript(customScript.c_str());
|
||||
|
@ -310,7 +326,7 @@ static void mriBindingExecute()
|
|||
}
|
||||
|
||||
ruby_cleanup(0);
|
||||
|
||||
delete pluginLoader;
|
||||
shState->rtData().rqTermAck = true;
|
||||
}
|
||||
|
||||
|
|
7
mkxp.pro
7
mkxp.pro
|
@ -6,6 +6,7 @@ TARGET = mkxp
|
|||
DEPENDPATH += src shader assets
|
||||
INCLUDEPATH += . src
|
||||
LIBS += -lGL
|
||||
QMAKE_LFLAGS += -rdynamic
|
||||
|
||||
CONFIG(release, debug|release): DEFINES += NDEBUG
|
||||
|
||||
|
@ -116,7 +117,8 @@ HEADERS += \
|
|||
src/sharedstate.h \
|
||||
src/al-util.h \
|
||||
src/boost-hash.h \
|
||||
src/debugwriter.h
|
||||
src/debugwriter.h \
|
||||
src/plugin.h
|
||||
|
||||
SOURCES += \
|
||||
src/main.cpp \
|
||||
|
@ -144,7 +146,8 @@ SOURCES += \
|
|||
src/config.cpp \
|
||||
src/tileatlas.cpp \
|
||||
src/perftimer.cpp \
|
||||
src/sharedstate.cpp
|
||||
src/sharedstate.cpp \
|
||||
src/plugin.cpp
|
||||
|
||||
EMBED = \
|
||||
shader/transSimple.frag \
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
|
||||
#include "plugin.h"
|
||||
#include "debugwriter.h"
|
||||
#include "exception.h"
|
||||
|
||||
#include "binding-util.h"
|
||||
|
||||
static void pluginInit();
|
||||
static void pluginFini();
|
||||
|
||||
Plugin plugin =
|
||||
{
|
||||
pluginInit,
|
||||
pluginFini,
|
||||
PLUGIN_MRI
|
||||
};
|
||||
|
||||
/* Callable from ruby scripts as 'plugin_function' */
|
||||
RB_METHOD(pluginFunction)
|
||||
{
|
||||
RB_UNUSED_PARAM;
|
||||
|
||||
const char *str = 0;
|
||||
rb_get_args(argc, argv, "|z", &str RB_ARG_END);
|
||||
|
||||
Debug() << "Sample plugin function:" << str;
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
void pluginInit()
|
||||
{
|
||||
_rb_define_module_function(rb_mKernel, "plugin_function", pluginFunction);
|
||||
|
||||
Debug() << "Sample plugin inited!";
|
||||
}
|
||||
|
||||
void pluginFini()
|
||||
{
|
||||
Debug() << "Sample plugin finited!";
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
######################################################################
|
||||
# Automatically generated by qmake (2.01a) Fri Jan 3 01:40:45 2014
|
||||
######################################################################
|
||||
|
||||
TEMPLATE = lib
|
||||
QT =
|
||||
TARGET = sample-plugin
|
||||
DEPENDPATH += . ../src ../binding-mri
|
||||
INCLUDEPATH += . ../src ../binding-mri
|
||||
CONFIG += plugin
|
||||
|
||||
unix {
|
||||
CONFIG += link_pkgconfig
|
||||
PKGCONFIG += ruby-2.1
|
||||
}
|
||||
|
||||
# Input
|
||||
SOURCES += main.cpp
|
|
@ -81,6 +81,7 @@ void Config::read()
|
|||
podesc.add_options()
|
||||
PO_DESC_ALL
|
||||
("RTP", po::value<StringVec>())
|
||||
("plugin", po::value<StringVec>())
|
||||
;
|
||||
|
||||
std::ifstream confFile;
|
||||
|
@ -103,6 +104,7 @@ void Config::read()
|
|||
PO_DESC_ALL;
|
||||
|
||||
GUARD_ALL( rtps = vm["RTP"].as<StringVec>(); );
|
||||
GUARD_ALL( plugins = vm["plugin"].as<StringVec>(); );
|
||||
|
||||
#undef PO_DESC
|
||||
#undef PO_DESC_ALL
|
||||
|
|
|
@ -53,6 +53,8 @@ struct Config
|
|||
std::string customScript;
|
||||
std::vector<std::string> rtps;
|
||||
|
||||
std::vector<std::string> plugins;
|
||||
|
||||
/* Game INI contents */
|
||||
struct {
|
||||
std::string scripts;
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
** plugin.cpp
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "plugin.h"
|
||||
|
||||
#include "sharedstate.h"
|
||||
#include "config.h"
|
||||
#include "exception.h"
|
||||
|
||||
#include <SDL_loadso.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
struct LoadedPlugin
|
||||
{
|
||||
void *handle;
|
||||
Plugin *iface;
|
||||
bool inited;
|
||||
};
|
||||
|
||||
struct PluginLoaderPrivate
|
||||
{
|
||||
std::vector<LoadedPlugin> loadedPlugins;
|
||||
|
||||
/* Load so files / lookup interfaces */
|
||||
void loadPlugins(const std::vector<std::string> &plgList,
|
||||
const char *binding)
|
||||
{
|
||||
for (size_t i = 0; i < plgList.size(); ++i)
|
||||
{
|
||||
const std::string &plgName = plgList[i];
|
||||
|
||||
void *handle = SDL_LoadObject(plgName.c_str());
|
||||
|
||||
if (!handle)
|
||||
throw Exception(Exception::SDLError, "%s", SDL_GetError());
|
||||
|
||||
Plugin *iface = static_cast<Plugin*>(SDL_LoadFunction(handle, "plugin"));
|
||||
|
||||
if (!iface)
|
||||
throw Exception(Exception::SDLError, "Error loading plugin '%s' interface: %s",
|
||||
plgName.c_str(), SDL_GetError());
|
||||
|
||||
if (strcmp(binding, iface->binding))
|
||||
throw Exception(Exception::MKXPError, "Plugin '%s' is incompatible with '%s' binding",
|
||||
plgName.c_str(), binding);
|
||||
|
||||
LoadedPlugin loaded;
|
||||
loaded.handle = handle;
|
||||
loaded.iface = iface;
|
||||
loaded.inited = false;
|
||||
|
||||
loadedPlugins.push_back(loaded);
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize loaded plugins */
|
||||
void initPlugins()
|
||||
{
|
||||
for (size_t i = 0; i < loadedPlugins.size(); ++i)
|
||||
{
|
||||
LoadedPlugin &plugin = loadedPlugins[i];
|
||||
|
||||
plugin.iface->init();
|
||||
plugin.inited = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Finalize loaded plugins */
|
||||
void finiPlugins()
|
||||
{
|
||||
for (size_t i = 0; i < loadedPlugins.size(); ++i)
|
||||
{
|
||||
LoadedPlugin &plugin = loadedPlugins[i];
|
||||
|
||||
if (plugin.inited)
|
||||
plugin.iface->fini();
|
||||
}
|
||||
}
|
||||
|
||||
/* Unload so files */
|
||||
void unloadPlugins()
|
||||
{
|
||||
for (size_t i = 0; i < loadedPlugins.size(); ++i)
|
||||
SDL_UnloadObject(loadedPlugins[i].handle);
|
||||
}
|
||||
};
|
||||
|
||||
PluginLoader::PluginLoader(const char *binding)
|
||||
{
|
||||
p = new PluginLoaderPrivate;
|
||||
|
||||
const std::vector<std::string> &plgList = shState->config().plugins;
|
||||
|
||||
try
|
||||
{
|
||||
p->loadPlugins(plgList, binding);
|
||||
}
|
||||
catch (const Exception &exc)
|
||||
{
|
||||
p->unloadPlugins();
|
||||
delete p;
|
||||
|
||||
throw exc;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
p->initPlugins();
|
||||
}
|
||||
catch (const Exception &exc)
|
||||
{
|
||||
p->finiPlugins();
|
||||
p->unloadPlugins();
|
||||
delete p;
|
||||
|
||||
throw exc;
|
||||
}
|
||||
}
|
||||
|
||||
PluginLoader::~PluginLoader()
|
||||
{
|
||||
p->finiPlugins();
|
||||
p->unloadPlugins();
|
||||
delete p;
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
** plugin.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PLUGIN_H
|
||||
#define PLUGIN_H
|
||||
|
||||
/* Note: Plugins are meant to be compiled in unison
|
||||
* with mkxp; there is no API stability whatsoever. */
|
||||
|
||||
/* Use one of these for 'binding' */
|
||||
#define PLUGIN_MRI "mri"
|
||||
#define PLUGIN_MRUBY "mruby"
|
||||
|
||||
/* Interface that plugins implement via global symbol named 'plugin'*/
|
||||
struct Plugin
|
||||
{
|
||||
/* Initialize plugin. May throw 'Exception' instance on error.
|
||||
* The interpreted environment is set up at this point. */
|
||||
void (*init)();
|
||||
|
||||
/* Finalize plugin. The interpreted environment
|
||||
* is already torn down at this point. */
|
||||
void (*fini)();
|
||||
|
||||
/* Binding that this plugin applies to (see above) */
|
||||
const char *binding;
|
||||
};
|
||||
|
||||
|
||||
/* For use in main mkxp */
|
||||
struct PluginLoaderPrivate;
|
||||
|
||||
class PluginLoader
|
||||
{
|
||||
public:
|
||||
PluginLoader(const char *binding);
|
||||
~PluginLoader();
|
||||
|
||||
private:
|
||||
PluginLoaderPrivate *p;
|
||||
};
|
||||
|
||||
#endif // PLUGIN_H
|
Loading…
Reference in New Issue