1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-07 12:30:27 +00:00

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:
Fredia Huya-Kouadio
2021-02-15 12:24:53 -08:00
parent a59286f019
commit fe9f0758db
4 changed files with 154 additions and 32 deletions

View File

@@ -46,7 +46,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()); 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,26 +168,29 @@ 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;
} }
/** /**
* Invoked once during the Godot Android initialization process after creation of the * Invoked once during the Godot Android initialization process after creation of the
* {@link org.godotengine.godot.GodotView} view. * {@link org.godotengine.godot.GodotRenderView} view.
* <p> * <p>
* The plugin can return a non-null {@link View} layout in order to add it to the Godot view * The plugin can return a non-null {@link View} layout in order to add it to the Godot view
* hierarchy. * hierarchy.
@@ -290,8 +328,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 {
@@ -301,6 +339,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();
@@ -317,12 +376,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) {
@@ -335,7 +390,7 @@ public abstract class GodotPlugin {
* Used to setup a {@link GodotPlugin} instance. * Used to setup a {@link GodotPlugin} instance.
* @param p_name Name of the instance. * @param p_name Name of the instance.
*/ */
private native void nativeRegisterSingleton(String p_name); private static native void nativeRegisterSingleton(String p_name, Object object);
/** /**
* Used to complete registration of the {@link GodotPlugin} instance's methods. * Used to complete registration of the {@link GodotPlugin} instance's methods.
@@ -344,13 +399,13 @@ public abstract class GodotPlugin {
* @param p_ret Return type of the registered method * @param p_ret Return type of the registered method
* @param p_params Method parameters types * @param p_params Method parameters types
*/ */
private native void nativeRegisterMethod(String p_sname, String p_name, String p_ret, String[] p_params); private static native void nativeRegisterMethod(String p_sname, String p_name, String p_ret, String[] p_params);
/** /**
* 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.
@@ -358,7 +413,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.
@@ -366,5 +421,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);
} }

View File

@@ -0,0 +1,67 @@
/*************************************************************************/
/* 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();
}

View File

@@ -41,7 +41,7 @@ static HashMap<String, JNISingleton *> jni_singletons;
extern "C" { extern "C" {
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jobject obj, jstring name) { JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jclass clazz, jstring name, jobject obj) {
String singname = jstring_to_string(name, env); String singname = jstring_to_string(name, env);
JNISingleton *s = (JNISingleton *)ClassDB::instance("JNISingleton"); JNISingleton *s = (JNISingleton *)ClassDB::instance("JNISingleton");
s->set_instance(env->NewGlobalRef(obj)); s->set_instance(env->NewGlobalRef(obj));
@@ -51,7 +51,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegis
ProjectSettings::get_singleton()->set(singname, s); ProjectSettings::get_singleton()->set(singname, s);
} }
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterMethod(JNIEnv *env, jobject obj, 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) {
String singname = jstring_to_string(sname, env); String singname = jstring_to_string(sname, env);
ERR_FAIL_COND(!jni_singletons.has(singname)); ERR_FAIL_COND(!jni_singletons.has(singname));
@@ -83,7 +83,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));
@@ -104,7 +104,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));
@@ -129,7 +129,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;

View File

@@ -35,11 +35,11 @@
#include <jni.h> #include <jni.h>
extern "C" { extern "C" {
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jobject obj, jstring name); 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, jobject obj, 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