Browse Source

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.
Fredia Huya-Kouadio 4 years ago
parent
commit
fe9f0758db

+ 77 - 22
platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java

@@ -46,7 +46,9 @@ import androidx.annotation.Nullable;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -107,14 +109,47 @@ public abstract class GodotPlugin {
 	 * This method is invoked on the render thread.
 	 */
 	public final void onRegisterPluginWithGodotNative() {
-		nativeRegisterSingleton(getPluginName());
+		registeredSignals.putAll(registerPluginWithGodotNative(this, new GodotPluginInfoProvider() {
+			@NonNull
+			@Override
+			public String getPluginName() {
+				return GodotPlugin.this.getPluginName();
+			}
+
+			@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();
+			}
+		}));
+	}
 
-		Class clazz = getClass();
+	/**
+	 * 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();
 		for (Method method : methods) {
 			boolean found = false;
 
-			for (String s : getPluginMethods()) {
+			for (String s : pluginInfoProvider.getPluginMethods()) {
 				if (s.equals(method.getName())) {
 					found = true;
 					break;
@@ -123,7 +158,7 @@ public abstract class GodotPlugin {
 			if (!found)
 				continue;
 
-			List<String> ptr = new ArrayList<String>();
+			List<String> ptr = new ArrayList<>();
 
 			Class[] paramTypes = method.getParameterTypes();
 			for (Class c : paramTypes) {
@@ -133,26 +168,29 @@ public abstract class GodotPlugin {
 			String[] pt = new String[ptr.size()];
 			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.
-		for (SignalInfo signalInfo : getPluginSignals()) {
+		Map<String, SignalInfo> registeredSignals = new HashMap<>();
+		for (SignalInfo signalInfo : pluginInfoProvider.getPluginSignals()) {
 			String signalName = signalInfo.getName();
-			nativeRegisterSignal(getPluginName(), signalName, signalInfo.getParamTypesNames());
+			nativeRegisterSignal(pluginInfoProvider.getPluginName(), signalName, signalInfo.getParamTypesNames());
 			registeredSignals.put(signalName, signalInfo);
 		}
 
 		// Get the list of gdnative libraries to register.
-		Set<String> gdnativeLibrariesPaths = getPluginGDNativeLibrariesPaths();
+		Set<String> gdnativeLibrariesPaths = pluginInfoProvider.getPluginGDNativeLibrariesPaths();
 		if (!gdnativeLibrariesPaths.isEmpty()) {
 			nativeRegisterGDNativeLibraries(gdnativeLibrariesPaths.toArray(new String[0]));
 		}
+
+		return registeredSignals;
 	}
 
 	/**
 	 * 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>
 	 * The plugin can return a non-null {@link View} layout in order to add it to the Godot view
 	 * hierarchy.
@@ -290,8 +328,8 @@ public abstract class GodotPlugin {
 
 	/**
 	 * Emit a registered Godot signal.
-	 * @param signalName
-	 * @param signalArgs
+	 * @param signalName Name of the signal to emit. It will be validated against the set of registered signals.
+	 * @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) {
 		try {
@@ -301,6 +339,27 @@ public abstract class GodotPlugin {
 				throw new IllegalArgumentException(
 						"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.
 			Class<?>[] signalParamTypes = signalInfo.getParamTypes();
@@ -317,12 +376,8 @@ public abstract class GodotPlugin {
 				}
 			}
 
-			runOnRenderThread(new Runnable() {
-				@Override
-				public void run() {
-					nativeEmitSignal(getPluginName(), signalName, signalArgs);
-				}
-			});
+			godot.runOnRenderThread(() -> nativeEmitSignal(pluginName, signalInfo.getName(), signalArgs));
+
 		} catch (IllegalArgumentException exception) {
 			Log.w(TAG, exception.getMessage());
 			if (BuildConfig.DEBUG) {
@@ -335,7 +390,7 @@ public abstract class GodotPlugin {
 	 * Used to setup a {@link GodotPlugin} 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.
@@ -344,13 +399,13 @@ public abstract class GodotPlugin {
 	 * @param p_ret Return type of the registered method
 	 * @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.
 	 * @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.
@@ -358,7 +413,7 @@ public abstract class GodotPlugin {
 	 * @param signalName Name of the signal to register
 	 * @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.
@@ -366,5 +421,5 @@ public abstract class GodotPlugin {
 	 * @param signalName Name of the signal to emit
 	 * @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);
 }

+ 67 - 0
platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginInfoProvider.java

@@ -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();
+}

+ 5 - 5
platform/android/plugin/godot_plugin_jni.cpp

@@ -41,7 +41,7 @@ static HashMap<String, JNISingleton *> jni_singletons;
 
 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);
 	JNISingleton *s = (JNISingleton *)ClassDB::instance("JNISingleton");
 	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);
 }
 
-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);
 
 	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));
 }
 
-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);
 
 	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);
 }
 
-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);
 
 	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);
 }
 
-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);
 	if (gdnlib_count == 0) {
 		return;

+ 5 - 5
platform/android/plugin/godot_plugin_jni.h

@@ -35,11 +35,11 @@
 #include <jni.h>
 
 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_nativeRegisterMethod(JNIEnv *env, jobject obj, 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_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_nativeRegisterGDNativeLibraries(JNIEnv *env, jobject obj, jobjectArray gdnlib_paths);
+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_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, 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, jclass clazz, jobjectArray gdnlib_paths);
 }
 
 #endif // GODOT_PLUGIN_JNI_H