CppPlugins/src/main/cpp/src/libplugin.cpp

342 lines
11 KiB
C++

#include "de_mrbesen_cppplugins_CppPlugin.h"
#include "de_mrbesen_cppplugins_CppPlugin_CppListener.h"
#include <iostream>
#include <dlfcn.h> //dynamic linking
#include <libgen.h> //basename
#include <thread> //thread id
#include "libplugin.h"
#include "plugin.h"
namespace Plugin {
jclass CPPPLUGINCLASS;
jclass EVENTCLASS;
jclass MAPCLASS;
jclass LISTENERCLASS;
jclass CMDCLASS;
jclass BUKKITCLASS;
jclass CBSERVERCLASS;
jclass CMDMAPCLASS;
jclass CMDSENDERCLASS;
jfieldID EVENTDATAF;
jfieldID EVENTNAMEF;
jfieldID PLUGINIDF;
jfieldID BUKKITSERVER;
jfieldID SERVERCMDMAP;
jmethodID MAPGET;
jmethodID MAPPUT;
jmethodID MAPCLEAR;
jmethodID LISTENERCONTR;
jmethodID LISTENERSMALLCONTR;
jmethodID CMDCONSTR;
jmethodID CMDMAPREGISTER;
jmethodID CMDSENDERSEND;
std::vector<CppPlugin*> plugins;
/*
void fixcap(JNIEnv* env) {
DEB("ensure 1024" );
env->EnsureLocalCapacity(1024);
DEB("push frame 1024" );
env->PushLocalFrame(1024);
}*/
CppPlugin* getPlugin(JNIEnv* env, jobject thisobj, int id) {
//get id
if(id < 0) {
load(env, JNI_PLUGINIDFIELD);
id = env->GetIntField(thisobj, PLUGINIDF);
}
return plugins.at(id);
}
void load(JNIEnv* env, long data) {
if(data & (JNI_PLUGINCLASS | JNI_PLUGINIDFIELD))
CPPPLUGINCLASS = env->FindClass("de/mrbesen/cppplugins/CppPlugin");
if(data & (JNI_EVENTCLASS | JNI_EVENTDATAFIELD | JNI_EVENTNAMEFIELD))
EVENTCLASS = env->FindClass("de/mrbesen/cppplugins/CppEvent");
if(data & (JNI_MAPCLASS | JNI_MAPCLEARMETH | JNI_MAPGETMETH | JNI_MAPPUTMETH))
MAPCLASS = env->FindClass("java/util/Map");
if(data & (JNI_LISTENERCLASS | JNI_LISTENERCONSTR | JNI_LISTENERSMALLCONSTR))
LISTENERCLASS = env->FindClass("de/mrbesen/cppplugins/CppPlugin$CppListener");
if(data & (JNI_CMDCLASS | JNI_CMDCONSTR))
CMDCLASS = env->FindClass("de/mrbesen/cppplugins/CppPlugin$CppCommand");
if(data & JNI_BUKKITCLASS)
BUKKITCLASS = env->FindClass("org/bukkit/Bukkit");
if(data & (JNI_CBSERVERCLASS | JNI_SERVERCMDMAPFIELD))
CBSERVERCLASS = env->FindClass("org/bukkit/craftbukkit/v1_12_R1/CraftServer");
if(data & (JNI_CMDMAPCLASS | JNI_CMDMAPREGISTERMETH))
CMDMAPCLASS = env->FindClass("org/bukkit/command/SimpleCommandMap");
if(data & (JNI_CMDSENDERCLASS | JNI_CMDSENDERSENDMETH))
CMDSENDERCLASS = env->FindClass("org/bukkit/command/CommandSender");
if(data & JNI_EVENTDATAFIELD)
EVENTDATAF = env->GetFieldID(EVENTCLASS, "data", "Ljava/util/Map;");
if(data & JNI_EVENTNAMEFIELD)
EVENTNAMEF = env->GetFieldID(EVENTCLASS, "name", "Ljava/lang/String;");
if(data & JNI_PLUGINIDFIELD)
PLUGINIDF = env->GetFieldID(CPPPLUGINCLASS, "id", "I");
if(data & JNI_BUKKITSERVFIELD)
BUKKITSERVER = env->GetStaticFieldID(BUKKITCLASS, "server", "Lorg/bukkit/Server;");
if(data & JNI_SERVERCMDMAPFIELD)
SERVERCMDMAP = env->GetFieldID(CBSERVERCLASS, "commandMap", "Lorg/bukkit/command/SimpleCommandMap;");
if(data & JNI_MAPGETMETH)
MAPGET = env->GetMethodID(MAPCLASS, "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
if(data & JNI_MAPPUTMETH)
MAPPUT = env->GetMethodID(MAPCLASS, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
if(data & JNI_MAPCLEARMETH)
MAPCLEAR = env->GetMethodID(MAPCLASS, "clear", "()V");
if(data & JNI_LISTENERCONSTR)
LISTENERCONTR = env->GetMethodID(LISTENERCLASS, "<init>", "(Ljava/lang/String;J)V");
if(data & JNI_LISTENERSMALLCONSTR)
LISTENERSMALLCONTR = env->GetMethodID(LISTENERCLASS, "<init>", "()V");
if(data & JNI_CMDCONSTR)
CMDCONSTR = env->GetMethodID(CMDCLASS, "<init>", "(Ljava/lang/String;JI)V");
if(data & JNI_CMDMAPREGISTERMETH)
CMDMAPREGISTER = env->GetMethodID(CMDMAPCLASS, "register", "(Ljava/lang/String;Lorg/bukkit/command/Command;)Z");
if(data & JNI_CMDSENDERSENDMETH)
CMDSENDERSEND = env->GetMethodID(CMDSENDERCLASS, "sendMessage", "(Ljava/lang/String;)V");
if(((!CPPPLUGINCLASS) + (!EVENTCLASS) + (!MAPCLASS) + (!LISTENERCLASS) + (!CMDCLASS) + (!BUKKITCLASS) + (!CBSERVERCLASS) + (!CMDMAPCLASS)//wenn eins == nullptr ist -> wird zu 1 -> endergebniss != 0 -> fehler
+ (!EVENTDATAF) + (!EVENTNAMEF) + (!PLUGINIDF) + (!BUKKITSERVER) + (!SERVERCMDMAP)
+ (!MAPGET) + (!MAPPUT) + (!MAPCLEAR) + (!LISTENERCONTR) + (!LISTENERSMALLCONTR) + (!CMDCONSTR) + (!CMDMAPREGISTER)) != 0) {
DEB(CRED << "Error loading a Java Information:");
DEB("find classes: " << (!!CPPPLUGINCLASS) << (!!EVENTCLASS) << (!!MAPCLASS) << (!!LISTENERCLASS) << (!!CMDCLASS) << (!!BUKKITCLASS) << (!!CBSERVERCLASS) << (!!CMDMAPCLASS));
DEB("find fields: " << (!!EVENTDATAF) << (!!EVENTNAMEF) << (!!PLUGINIDF) << (!!BUKKITSERVER) << (!!SERVERCMDMAP));
DEB("find methods: " << (!!MAPGET) << (!!MAPPUT) << (!!MAPCLEAR) << (!!LISTENERCONTR) << (!!LISTENERSMALLCONTR) << (!!CMDCONSTR) << (!!CMDMAPREGISTER));
}
}
bool registerCmd(JNIEnv* env, CppPlugin* plugin, std::string& cname, cmdfptr function) {
DEB(CYELLOW << "Register Cmd " << CGREEN << cname << CYELLOW << " for " << CGREEN << plugin->name);
load(env, JNI_BUKKITCLASS | JNI_SERVERCMDMAPFIELD | JNI_CMDCONSTR | JNI_CMDMAPREGISTERMETH);
// create cnd
jstring name = env->NewStringUTF(cname.c_str());
jobject cmd = env->NewObject(CMDCLASS, CMDCONSTR, name, reinterpret_cast<jlong>(function), plugin->id);
env->DeleteLocalRef(name);
//call register
// ((CraftServer) Bukkit.getServer()).getCommandMap().register(fallbackprefix, cmd);
jobject jserver = env->GetStaticObjectField(BUKKITCLASS, BUKKITSERVER);
jobject cmdmap = env->GetObjectField(jserver, SERVERCMDMAP);
jstring fallbackprefix = env->NewStringUTF(plugin->name.c_str());
return env->CallBooleanMethod(cmdmap, CMDMAPREGISTER, fallbackprefix, cmd);
}
void send(JNIEnv* env, jobject sender, const char* text) {
load(env, JNI_CMDSENDERSENDMETH);
jstring jtext = env->NewStringUTF(text);
env->CallVoidMethod(sender, CMDSENDERSEND, jtext);
env->DeleteLocalRef(jtext);
}
} //namespace
JNIEXPORT jboolean JNICALL Java_de_mrbesen_cppplugins_CppPlugin_init_1(JNIEnv* env, jclass pluginclass) {
Plugin::load(env, Plugin::JNI_ALL);
return true;
}
/**
* Class: de_mrbesen_cppplugins_CppPlugin
* Method: loadPlugin
* Signature: (Ljava/lang/String;)Z
*/
JNIEXPORT jboolean JNICALL Java_de_mrbesen_cppplugins_CppPlugin_loadPlugin(JNIEnv* env, jobject thisobj, jstring path) {
const char* cpath = env->GetStringUTFChars(path, 0);
//open plugin
DEB(CBLUE << "open plugin " << cpath);
void* handle = dlopen(cpath, RTLD_LAZY);
if (!handle) {
std::cout << "could not create handle of: " << cpath << " error: " << dlerror() << std::endl;
return false;
}
//throw std::string("Error: plugin could not be opend. ") + dlerror();
//load function pointer
Plugin::initfptr initf = (Plugin::initfptr) dlsym(handle, "init");
if(!initf) {
std::cout << "Could not find function init: " << dlerror() << std::endl;
dlclose(handle);
return false;
}
Plugin::CppPlugin* plugin = initf();
DEB(CGREY << "Initilized");
plugin->handle = handle;
//set name / handle id
plugin->name = std::string(cpath);
plugin->name = plugin->name.substr(plugin->name.rfind('/')+1);
DEB(CGREY << "name set");
// add to vector
Plugin::plugins.push_back(plugin);
int id = Plugin::plugins.size()-1;
plugin->id = id;
DEB(CGREY << "plugin added to vector");
//store id
Plugin::load(env,Plugin:: JNI_ALL);
env->SetIntField(thisobj,Plugin:: PLUGINIDF, id);
DEB(CGREY << "Internal id set");
return (bool) plugin; // converted to boolean here
}
/*
* Class: de_mrbesen_cppplugins_CppPlugin
* Method: onLoad
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_de_mrbesen_cppplugins_CppPlugin_onLoad(JNIEnv* env, jobject thisobj) {
DEB(CGREY << "lib::onLoad()");
Plugin::getPlugin(env, thisobj)->onLoad(env);
}
/*
* Class: de_mrbesen_cppplugins_CppPlugin
* Method: onEnable_
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_de_mrbesen_cppplugins_CppPlugin_onEnable_1(JNIEnv* env, jobject thisobj) {
DEB(CGREY << "lib::onEnable()");
Plugin::getPlugin(env, thisobj)->onEnable(env);
}
/*
* Class: de_mrbesen_cppplugins_CppPlugin
* Method: onDisable_
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_de_mrbesen_cppplugins_CppPlugin_onDisable_1(JNIEnv* env, jobject thisobj) {
//TODO
DEB(CGREY << "lib::onDisable()");
Plugin::CppPlugin* plugin = Plugin::getPlugin(env, thisobj);
plugin->onDisable(env);
dlclose(plugin->handle);
}
/*
* Class: de_mrbesen_cppplugins_CppPlugin
* Method: fireEvent
* Signature: (Lde/mrbesen/cppplugins/CppEvent;I)Lde/mrbesen/cppplugins/CppEvent;
*/
JNIEXPORT jobject JNICALL Java_de_mrbesen_cppplugins_CppPlugin_fireEvent(JNIEnv* env, jobject thisobj, jobject jevent, jlong functionid) {
DEB(CGREY << "lib::fireEvent()");
if(functionid == 0) {
DEB(CRED << "invalid function id");
return jevent;
}
Plugin::load(env, Plugin::JNI_EVENT | Plugin::JNI_MAP);
//cast function
Plugin::eventfptr f = reinterpret_cast<Plugin::eventfptr>(functionid);
//cast event
Plugin::Event event(env, jevent);
//get Plugin
Plugin::CppPlugin* plugin = Plugin::getPlugin(env, thisobj);
//call it
try {
f(env, plugin, &event);
} catch(...) {
std::cout << CRED << "Error while processing Event " << event.getName() << " in plugin: " << plugin->name << CRESET << std::endl;
return jevent;
}
//put changed fields back in the jevent
event.reapply(env, jevent);
return jevent;
}
/*
* Class: de_mrbesen_cppplugins_CppPlugin
* Method: onCmd
* Signature: (Lorg/bukkit/command/CommandSender;Lorg/bukkit/command/Command;Ljava/lang/String;[Ljava/lang/String;J)Z
*/
JNIEXPORT jboolean JNICALL Java_de_mrbesen_cppplugins_CppPlugin_onCmd(JNIEnv* env, jclass cppplugin, jobject sender, jobject cmd, jstring label, jobjectArray args, jlong fid, jint pluginid) {
if(fid == 0) return false;
Plugin::CppPlugin* plugin = Plugin::getPlugin(pluginid);
//parse args
int argsize = env->GetArrayLength(args);
std::vector<std::string> cargs(argsize);
for(int i = 0; i < argsize; i++) {
jstring ar = (jstring) env->GetObjectArrayElement(args, i);
cargs[i] = env->GetStringUTFChars(ar, 0);
}
//parse label
std::string clabel = env->GetStringUTFChars(label, 0);
//call function
Plugin::cmdfptr cmdf = reinterpret_cast<Plugin::cmdfptr>(fid);
bool result = false;
try {
result = cmdf(env, plugin, sender, cmd, clabel, cargs);
} catch(...) {
std::cout << CRED << "Error while executing Command " << clabel << " in plugin: " << plugin->name << CRESET << std::endl;
return false;
}
return result;
}
/*
* Class: de_mrbesen_cppplugins_CppPlugin
* Method: getListener
* Signature: ()[Lde/mrbesen/cppplugins/CppPlugin/CppListener;
*/
JNIEXPORT jobjectArray JNICALL Java_de_mrbesen_cppplugins_CppPlugin_getListener(JNIEnv* env, jobject thisobj) {
Plugin::load(env, Plugin::JNI_LISTENER);
//call getEvents
std::map<std::string, Plugin::eventfptr> events = Plugin::getPlugin(env, thisobj)->getEvents();
DEB(CYELLOW << "found " << events.size() << " Events" );
//create array
jobject initial = env->NewObject(Plugin::LISTENERCLASS, Plugin::LISTENERSMALLCONTR);
jobjectArray arr = env->NewObjectArray(events.size(), Plugin::LISTENERCLASS, initial);
DEB(CGREY << "populate array" );
//populate array
int i = 0;
for(auto it : events) {
//create object
jstring eventname = env->NewStringUTF(it.first.c_str());
jobject listener = env->NewObject(Plugin::LISTENERCLASS, Plugin::LISTENERCONTR, eventname, it.second);
env->DeleteLocalRef(eventname);
//set array
env->SetObjectArrayElement(arr, i++, listener);
}
DEB(CGREY << "Array finished" );
return arr;
}