Expose GodotPlugin's utility methods used for registration and signal emitting.
This enables creation and use of a plugin like class by composition rather than inheritance.
This commit is contained in:
parent
daf1151b79
commit
64f5a7b8ca
4 changed files with 148 additions and 25 deletions
|
@ -45,7 +45,9 @@ import androidx.annotation.Nullable;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
@ -107,14 +109,47 @@ public abstract class GodotPlugin {
|
||||||
* This method is invoked on the render thread.
|
* This method is invoked on the render thread.
|
||||||
*/
|
*/
|
||||||
public final void onRegisterPluginWithGodotNative() {
|
public final void onRegisterPluginWithGodotNative() {
|
||||||
nativeRegisterSingleton(getPluginName(), this);
|
registeredSignals.putAll(registerPluginWithGodotNative(this, new GodotPluginInfoProvider() {
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String getPluginName() {
|
||||||
|
return GodotPlugin.this.getPluginName();
|
||||||
|
}
|
||||||
|
|
||||||
Class clazz = getClass();
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public List<String> getPluginMethods() {
|
||||||
|
return GodotPlugin.this.getPluginMethods();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Set<SignalInfo> getPluginSignals() {
|
||||||
|
return GodotPlugin.this.getPluginSignals();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Set<String> getPluginGDNativeLibrariesPaths() {
|
||||||
|
return GodotPlugin.this.getPluginGDNativeLibrariesPaths();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the plugin with Godot native code.
|
||||||
|
*
|
||||||
|
* This method must be invoked on the render thread.
|
||||||
|
*/
|
||||||
|
public static Map<String, SignalInfo> registerPluginWithGodotNative(Object pluginObject, GodotPluginInfoProvider pluginInfoProvider) {
|
||||||
|
nativeRegisterSingleton(pluginInfoProvider.getPluginName(), pluginObject);
|
||||||
|
|
||||||
|
Class clazz = pluginObject.getClass();
|
||||||
Method[] methods = clazz.getDeclaredMethods();
|
Method[] methods = clazz.getDeclaredMethods();
|
||||||
for (Method method : methods) {
|
for (Method method : methods) {
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
|
|
||||||
for (String s : getPluginMethods()) {
|
for (String s : pluginInfoProvider.getPluginMethods()) {
|
||||||
if (s.equals(method.getName())) {
|
if (s.equals(method.getName())) {
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
|
@ -123,7 +158,7 @@ public abstract class GodotPlugin {
|
||||||
if (!found)
|
if (!found)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
List<String> ptr = new ArrayList<String>();
|
List<String> ptr = new ArrayList<>();
|
||||||
|
|
||||||
Class[] paramTypes = method.getParameterTypes();
|
Class[] paramTypes = method.getParameterTypes();
|
||||||
for (Class c : paramTypes) {
|
for (Class c : paramTypes) {
|
||||||
|
@ -133,21 +168,24 @@ public abstract class GodotPlugin {
|
||||||
String[] pt = new String[ptr.size()];
|
String[] pt = new String[ptr.size()];
|
||||||
ptr.toArray(pt);
|
ptr.toArray(pt);
|
||||||
|
|
||||||
nativeRegisterMethod(getPluginName(), method.getName(), method.getReturnType().getName(), pt);
|
nativeRegisterMethod(pluginInfoProvider.getPluginName(), method.getName(), method.getReturnType().getName(), pt);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register the signals for this plugin.
|
// Register the signals for this plugin.
|
||||||
for (SignalInfo signalInfo : getPluginSignals()) {
|
Map<String, SignalInfo> registeredSignals = new HashMap<>();
|
||||||
|
for (SignalInfo signalInfo : pluginInfoProvider.getPluginSignals()) {
|
||||||
String signalName = signalInfo.getName();
|
String signalName = signalInfo.getName();
|
||||||
nativeRegisterSignal(getPluginName(), signalName, signalInfo.getParamTypesNames());
|
nativeRegisterSignal(pluginInfoProvider.getPluginName(), signalName, signalInfo.getParamTypesNames());
|
||||||
registeredSignals.put(signalName, signalInfo);
|
registeredSignals.put(signalName, signalInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the list of gdnative libraries to register.
|
// Get the list of gdnative libraries to register.
|
||||||
Set<String> gdnativeLibrariesPaths = getPluginGDNativeLibrariesPaths();
|
Set<String> gdnativeLibrariesPaths = pluginInfoProvider.getPluginGDNativeLibrariesPaths();
|
||||||
if (!gdnativeLibrariesPaths.isEmpty()) {
|
if (!gdnativeLibrariesPaths.isEmpty()) {
|
||||||
nativeRegisterGDNativeLibraries(gdnativeLibrariesPaths.toArray(new String[0]));
|
nativeRegisterGDNativeLibraries(gdnativeLibrariesPaths.toArray(new String[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return registeredSignals;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -274,8 +312,8 @@ public abstract class GodotPlugin {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emit a registered Godot signal.
|
* Emit a registered Godot signal.
|
||||||
* @param signalName
|
* @param signalName Name of the signal to emit. It will be validated against the set of registered signals.
|
||||||
* @param signalArgs
|
* @param signalArgs Arguments used to populate the emitted signal. The arguments will be validated against the {@link SignalInfo} matching the registered signalName parameter.
|
||||||
*/
|
*/
|
||||||
protected void emitSignal(final String signalName, final Object... signalArgs) {
|
protected void emitSignal(final String signalName, final Object... signalArgs) {
|
||||||
try {
|
try {
|
||||||
|
@ -285,6 +323,27 @@ public abstract class GodotPlugin {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Signal " + signalName + " is not registered for this plugin.");
|
"Signal " + signalName + " is not registered for this plugin.");
|
||||||
}
|
}
|
||||||
|
emitSignal(getGodot(), getPluginName(), signalInfo, signalArgs);
|
||||||
|
} catch (IllegalArgumentException exception) {
|
||||||
|
Log.w(TAG, exception.getMessage());
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
throw exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit a Godot signal.
|
||||||
|
* @param godot
|
||||||
|
* @param pluginName Name of the Godot plugin the signal will be emitted from. The plugin must already be registered with the Godot engine.
|
||||||
|
* @param signalInfo Information about the signal to emit.
|
||||||
|
* @param signalArgs Arguments used to populate the emitted signal. The arguments will be validated against the given {@link SignalInfo} parameter.
|
||||||
|
*/
|
||||||
|
public static void emitSignal(Godot godot, String pluginName, SignalInfo signalInfo, final Object... signalArgs) {
|
||||||
|
try {
|
||||||
|
if (signalInfo == null) {
|
||||||
|
throw new IllegalArgumentException("Signal must be non null.");
|
||||||
|
}
|
||||||
|
|
||||||
// Validate the arguments count.
|
// Validate the arguments count.
|
||||||
Class<?>[] signalParamTypes = signalInfo.getParamTypes();
|
Class<?>[] signalParamTypes = signalInfo.getParamTypes();
|
||||||
|
@ -301,12 +360,8 @@ public abstract class GodotPlugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
runOnRenderThread(new Runnable() {
|
godot.runOnRenderThread(() -> nativeEmitSignal(pluginName, signalInfo.getName(), signalArgs));
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
nativeEmitSignal(getPluginName(), signalName, signalArgs);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (IllegalArgumentException exception) {
|
} catch (IllegalArgumentException exception) {
|
||||||
Log.w(TAG, exception.getMessage());
|
Log.w(TAG, exception.getMessage());
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
|
@ -334,7 +389,7 @@ public abstract class GodotPlugin {
|
||||||
* Used to register gdnative libraries bundled by the plugin.
|
* Used to register gdnative libraries bundled by the plugin.
|
||||||
* @param gdnlibPaths Paths to the libraries relative to the 'assets' directory.
|
* @param gdnlibPaths Paths to the libraries relative to the 'assets' directory.
|
||||||
*/
|
*/
|
||||||
private native void nativeRegisterGDNativeLibraries(String[] gdnlibPaths);
|
private static native void nativeRegisterGDNativeLibraries(String[] gdnlibPaths);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to complete registration of the {@link GodotPlugin} instance's methods.
|
* Used to complete registration of the {@link GodotPlugin} instance's methods.
|
||||||
|
@ -342,7 +397,7 @@ public abstract class GodotPlugin {
|
||||||
* @param signalName Name of the signal to register
|
* @param signalName Name of the signal to register
|
||||||
* @param signalParamTypes Signal parameters types
|
* @param signalParamTypes Signal parameters types
|
||||||
*/
|
*/
|
||||||
private native void nativeRegisterSignal(String pluginName, String signalName, String[] signalParamTypes);
|
private static native void nativeRegisterSignal(String pluginName, String signalName, String[] signalParamTypes);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to emit signal by {@link GodotPlugin} instance.
|
* Used to emit signal by {@link GodotPlugin} instance.
|
||||||
|
@ -350,5 +405,5 @@ public abstract class GodotPlugin {
|
||||||
* @param signalName Name of the signal to emit
|
* @param signalName Name of the signal to emit
|
||||||
* @param signalParams Signal parameters
|
* @param signalParams Signal parameters
|
||||||
*/
|
*/
|
||||||
private native void nativeEmitSignal(String pluginName, String signalName, Object[] signalParams);
|
private static native void nativeEmitSignal(String pluginName, String signalName, Object[] signalParams);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* GodotPluginInfoProvider.java */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||||
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
/*************************************************************************/
|
||||||
|
|
||||||
|
package org.godotengine.godot.plugin;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the set of information expected from a Godot plugin.
|
||||||
|
*/
|
||||||
|
public interface GodotPluginInfoProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the plugin.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
String getPluginName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of methods to be exposed to Godot.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
List<String> getPluginMethods();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of signals to be exposed to Godot.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
Set<SignalInfo> getPluginSignals();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the paths for the plugin's gdnative libraries (if any).
|
||||||
|
*
|
||||||
|
* The paths must be relative to the 'assets' directory and point to a '*.gdnlib' file.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
Set<String> getPluginGDNativeLibrariesPaths();
|
||||||
|
}
|
|
@ -87,7 +87,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegis
|
||||||
s->add_method(mname, mid, types, get_jni_type(retval));
|
s->add_method(mname, mid, types, get_jni_type(retval));
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSignal(JNIEnv *env, jobject obj, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_param_types) {
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSignal(JNIEnv *env, jclass clazz, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_param_types) {
|
||||||
String singleton_name = jstring_to_string(j_plugin_name, env);
|
String singleton_name = jstring_to_string(j_plugin_name, env);
|
||||||
|
|
||||||
ERR_FAIL_COND(!jni_singletons.has(singleton_name));
|
ERR_FAIL_COND(!jni_singletons.has(singleton_name));
|
||||||
|
@ -109,7 +109,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegis
|
||||||
singleton->add_signal(signal_name, types);
|
singleton->add_signal(signal_name, types);
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitSignal(JNIEnv *env, jobject obj, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_params) {
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitSignal(JNIEnv *env, jclass clazz, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_params) {
|
||||||
String singleton_name = jstring_to_string(j_plugin_name, env);
|
String singleton_name = jstring_to_string(j_plugin_name, env);
|
||||||
|
|
||||||
ERR_FAIL_COND(!jni_singletons.has(singleton_name));
|
ERR_FAIL_COND(!jni_singletons.has(singleton_name));
|
||||||
|
@ -134,7 +134,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitS
|
||||||
singleton->emit_signal(signal_name, args, count);
|
singleton->emit_signal(signal_name, args, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterGDNativeLibraries(JNIEnv *env, jobject obj, jobjectArray gdnlib_paths) {
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterGDNativeLibraries(JNIEnv *env, jclass clazz, jobjectArray gdnlib_paths) {
|
||||||
int gdnlib_count = env->GetArrayLength(gdnlib_paths);
|
int gdnlib_count = env->GetArrayLength(gdnlib_paths);
|
||||||
if (gdnlib_count == 0) {
|
if (gdnlib_count == 0) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -37,9 +37,9 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jclass clazz, jstring name, jobject obj);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jclass clazz, jstring name, jobject obj);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterMethod(JNIEnv *env, jclass clazz, jstring sname, jstring name, jstring ret, jobjectArray args);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterMethod(JNIEnv *env, jclass clazz, jstring sname, jstring name, jstring ret, jobjectArray args);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSignal(JNIEnv *env, jobject obj, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_param_types);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSignal(JNIEnv *env, jclass clazz, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_param_types);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitSignal(JNIEnv *env, jobject obj, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_params);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitSignal(JNIEnv *env, jclass clazz, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_params);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterGDNativeLibraries(JNIEnv *env, jobject obj, jobjectArray gdnlib_paths);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterGDNativeLibraries(JNIEnv *env, jclass clazz, jobjectArray gdnlib_paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // GODOT_PLUGIN_JNI_H
|
#endif // GODOT_PLUGIN_JNI_H
|
||||||
|
|
Loading…
Reference in a new issue