You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-06 12:20:30 +00:00
Godot Android plugin re-architecture
This commit is contained in:
@@ -907,6 +907,19 @@ class Godot(private val context: Context) : SensorEventListener {
|
||||
return PermissionsUtil.getGrantedPermissions(getActivity())
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of gdextension modules to register.
|
||||
*/
|
||||
@Keep
|
||||
private fun getGDExtensionConfigFiles(): Array<String> {
|
||||
val configFiles = mutableSetOf<String>()
|
||||
for (plugin in pluginRegistry.allPlugins) {
|
||||
configFiles.addAll(plugin.pluginGDExtensionLibrariesPaths)
|
||||
}
|
||||
|
||||
return configFiles.toTypedArray()
|
||||
}
|
||||
|
||||
@Keep
|
||||
private fun getCACertificates(): String {
|
||||
return GodotNetUtils.getCACertificates()
|
||||
|
||||
@@ -57,27 +57,25 @@ import javax.microedition.khronos.egl.EGLConfig;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
/**
|
||||
* Base class for the Godot Android plugins.
|
||||
* Base class for Godot Android plugins.
|
||||
* <p>
|
||||
* A Godot Android plugin is a regular Android library packaged as an aar archive file with the following caveats:
|
||||
* A Godot Android plugin is an Android library with the following requirements:
|
||||
* <p>
|
||||
* - The library must have a dependency on the Godot Android library (godot-lib.aar).
|
||||
* A stable version is available for each release.
|
||||
* - The library must have a 'compileOnly' dependency on the Godot Android library: `compileOnly "org.godotengine:godot:<godotLibVersion>"`
|
||||
* <p>
|
||||
* - The library must include a <meta-data> tag in its manifest file setup as follow:
|
||||
* <meta-data android:name="org.godotengine.plugin.v1.[PluginName]" android:value="[plugin.init.ClassFullName]" />
|
||||
* - The library must include a <meta-data> tag in its Android manifest with the following format:
|
||||
* <meta-data android:name="org.godotengine.plugin.v2.[PluginName]" android:value="[plugin.init.ClassFullName]" />
|
||||
* Where:
|
||||
* - 'PluginName' is the name of the plugin.
|
||||
* - 'plugin.init.ClassFullName' is the full name (package + class name) of the plugin class
|
||||
* - 'plugin.init.ClassFullName' is the full name (package + class name) of the plugin init class
|
||||
* extending {@link GodotPlugin}.
|
||||
* <p>
|
||||
* A Godot Android plugin can also define and provide c/c++ gdextension libraries, which will be
|
||||
* automatically bundled by the aar build system.
|
||||
* GDExtension ('*.gdextension') config files must be located in the project 'assets' directory and
|
||||
* their paths specified by {@link GodotPlugin#getPluginGDExtensionLibrariesPaths()}.
|
||||
*
|
||||
* A plugin can also define and provide c/c++ gdextension libraries and nativescripts for the target
|
||||
* app/game to leverage.
|
||||
* The shared library for the gdextension library will be automatically bundled by the aar build
|
||||
* system.
|
||||
* Godot '*.gdextension' resource files must however be manually defined in the project
|
||||
* 'assets' directory. The recommended path for these resources in the 'assets' directory should be:
|
||||
* 'godot/plugin/v1/[PluginName]/'
|
||||
* @see <a href="https://docs.godotengine.org/en/stable/tutorials/platform/android/index.html">Android plugins</a>
|
||||
*/
|
||||
public abstract class GodotPlugin {
|
||||
private static final String TAG = GodotPlugin.class.getSimpleName();
|
||||
@@ -97,7 +95,7 @@ public abstract class GodotPlugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides access to the underlying {@link Activity}.
|
||||
* Provides access to the hosting {@link Activity}.
|
||||
*/
|
||||
@Nullable
|
||||
protected Activity getActivity() {
|
||||
@@ -106,33 +104,16 @@ public abstract class GodotPlugin {
|
||||
|
||||
/**
|
||||
* Register the plugin with Godot native code.
|
||||
*
|
||||
* This method is invoked on the render thread.
|
||||
* <p>
|
||||
* This method is invoked by the Godot Engine on the render thread.
|
||||
*/
|
||||
public final void onRegisterPluginWithGodotNative() {
|
||||
registeredSignals.putAll(
|
||||
registerPluginWithGodotNative(this, getPluginName(), getPluginMethods(), getPluginSignals(),
|
||||
getPluginGDExtensionLibrariesPaths()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the plugin with Godot native code.
|
||||
*
|
||||
* This method must be invoked on the render thread.
|
||||
*/
|
||||
public static void registerPluginWithGodotNative(Object pluginObject,
|
||||
GodotPluginInfoProvider pluginInfoProvider) {
|
||||
registerPluginWithGodotNative(pluginObject, pluginInfoProvider.getPluginName(),
|
||||
Collections.emptyList(), pluginInfoProvider.getPluginSignals(),
|
||||
pluginInfoProvider.getPluginGDExtensionLibrariesPaths());
|
||||
|
||||
// Notify that registration is complete.
|
||||
pluginInfoProvider.onPluginRegistered();
|
||||
registerPluginWithGodotNative(this, getPluginName(), getPluginSignals()));
|
||||
}
|
||||
|
||||
private static Map<String, SignalInfo> registerPluginWithGodotNative(Object pluginObject,
|
||||
String pluginName, List<String> pluginMethods, Set<SignalInfo> pluginSignals,
|
||||
Set<String> pluginGDExtensionLibrariesPaths) {
|
||||
String pluginName, Set<SignalInfo> pluginSignals) {
|
||||
nativeRegisterSingleton(pluginName, pluginObject);
|
||||
|
||||
Set<Method> filteredMethods = new HashSet<>();
|
||||
@@ -143,14 +124,6 @@ public abstract class GodotPlugin {
|
||||
// Check if the method is annotated with {@link UsedByGodot}.
|
||||
if (method.getAnnotation(UsedByGodot.class) != null) {
|
||||
filteredMethods.add(method);
|
||||
} else {
|
||||
// For backward compatibility, process the methods from the given <pluginMethods> argument.
|
||||
for (String methodName : pluginMethods) {
|
||||
if (methodName.equals(method.getName())) {
|
||||
filteredMethods.add(method);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,23 +149,18 @@ public abstract class GodotPlugin {
|
||||
registeredSignals.put(signalName, signalInfo);
|
||||
}
|
||||
|
||||
// Get the list of gdextension libraries to register.
|
||||
if (!pluginGDExtensionLibrariesPaths.isEmpty()) {
|
||||
nativeRegisterGDExtensionLibraries(pluginGDExtensionLibrariesPaths.toArray(new String[0]));
|
||||
}
|
||||
|
||||
return registeredSignals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked once during the Godot Android initialization process after creation of the
|
||||
* Invoked once during the initialization process after creation of the
|
||||
* {@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
|
||||
* The plugin can return a non-null {@link View} layout which will be added to the Godot view
|
||||
* hierarchy.
|
||||
*
|
||||
* Use shouldBeOnTop() to set whether the plugin's {@link View} should be added on top or behind
|
||||
* the main Godot view.
|
||||
* <p>
|
||||
* Use {@link GodotPlugin#shouldBeOnTop()} to specify whether the plugin's {@link View} should
|
||||
* be added on top or behind the main Godot view.
|
||||
*
|
||||
* @see Activity#onCreate(Bundle)
|
||||
* @return the plugin's view to be included; null if no views should be included.
|
||||
@@ -235,44 +203,52 @@ public abstract class GodotPlugin {
|
||||
public boolean onMainBackPressed() { return false; }
|
||||
|
||||
/**
|
||||
* Invoked on the render thread when the Godot setup is complete.
|
||||
* Invoked on the render thread when set up of the Godot engine is complete.
|
||||
* <p>
|
||||
* This is invoked before {@link GodotPlugin#onGodotMainLoopStarted()}.
|
||||
*/
|
||||
public void onGodotSetupCompleted() {}
|
||||
|
||||
/**
|
||||
* Invoked on the render thread when the Godot main loop has started.
|
||||
*
|
||||
* This is invoked after {@link GodotPlugin#onGodotSetupCompleted()}.
|
||||
*/
|
||||
public void onGodotMainLoopStarted() {}
|
||||
|
||||
/**
|
||||
* Invoked once per frame on the GL thread after the frame is drawn.
|
||||
* When using the OpenGL renderer, this is invoked once per frame on the GL thread after the
|
||||
* frame is drawn.
|
||||
*/
|
||||
public void onGLDrawFrame(GL10 gl) {}
|
||||
|
||||
/**
|
||||
* Called on the GL thread after the surface is created and whenever the OpenGL ES surface size
|
||||
* changes.
|
||||
* When using the OpenGL renderer, this is called on the GL thread after the surface is created
|
||||
* and whenever the OpenGL ES surface size changes.
|
||||
*/
|
||||
public void onGLSurfaceChanged(GL10 gl, int width, int height) {}
|
||||
|
||||
/**
|
||||
* Called on the GL thread when the surface is created or recreated.
|
||||
* When using the OpenGL renderer, this is called on the GL thread when the surface is created
|
||||
* or recreated.
|
||||
*/
|
||||
public void onGLSurfaceCreated(GL10 gl, EGLConfig config) {}
|
||||
|
||||
/**
|
||||
* Invoked once per frame on the Vulkan thread after the frame is drawn.
|
||||
* When using the Vulkan renderer, this is invoked once per frame on the Vulkan thread after
|
||||
* the frame is drawn.
|
||||
*/
|
||||
public void onVkDrawFrame() {}
|
||||
|
||||
/**
|
||||
* Called on the Vulkan thread after the surface is created and whenever the surface size
|
||||
* changes.
|
||||
* When using the Vulkan renderer, this is called on the Vulkan thread after the surface is
|
||||
* created and whenever the surface size changes.
|
||||
*/
|
||||
public void onVkSurfaceChanged(Surface surface, int width, int height) {}
|
||||
|
||||
/**
|
||||
* Called on the Vulkan thread when the surface is created or recreated.
|
||||
* When using the Vulkan renderer, this is called on the Vulkan thread when the surface is
|
||||
* created or recreated.
|
||||
*/
|
||||
public void onVkSurfaceCreated(Surface surface) {}
|
||||
|
||||
@@ -284,17 +260,6 @@ public abstract class GodotPlugin {
|
||||
@NonNull
|
||||
public abstract String getPluginName();
|
||||
|
||||
/**
|
||||
* Returns the list of methods to be exposed to Godot.
|
||||
*
|
||||
* @deprecated Used the {@link UsedByGodot} annotation instead.
|
||||
*/
|
||||
@NonNull
|
||||
@Deprecated
|
||||
public List<String> getPluginMethods() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of signals to be exposed to Godot.
|
||||
*/
|
||||
@@ -304,19 +269,19 @@ public abstract class GodotPlugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the paths for the plugin's gdextension libraries.
|
||||
*
|
||||
* The paths must be relative to the 'assets' directory and point to a '*.gdextension' file.
|
||||
* Returns the paths for the plugin's gdextension libraries (if any).
|
||||
* <p>
|
||||
* Each returned path must be relative to the 'assets' directory and point to a '*.gdextension' file.
|
||||
*/
|
||||
@NonNull
|
||||
protected Set<String> getPluginGDExtensionLibrariesPaths() {
|
||||
public Set<String> getPluginGDExtensionLibrariesPaths() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the plugin's {@link View} returned in onMainCreate() should be placed on
|
||||
* top of the main Godot view.
|
||||
*
|
||||
* Returns whether the plugin's {@link View} returned in
|
||||
* {@link GodotPlugin#onMainCreate(Activity)} should be placed on top of the main Godot view.
|
||||
* <p>
|
||||
* Returning false causes the plugin's {@link View} to be placed behind, which can be useful
|
||||
* when used with transparency in order to let the Godot view handle inputs.
|
||||
*/
|
||||
@@ -359,7 +324,7 @@ public abstract class GodotPlugin {
|
||||
}
|
||||
emitSignal(getGodot(), getPluginName(), signalInfo, signalArgs);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
Log.w(TAG, exception.getMessage());
|
||||
Log.w(TAG, exception);
|
||||
if (BuildConfig.DEBUG) {
|
||||
throw exception;
|
||||
}
|
||||
@@ -368,7 +333,7 @@ public abstract class GodotPlugin {
|
||||
|
||||
/**
|
||||
* Emit a Godot signal.
|
||||
* @param godot
|
||||
* @param godot Godot instance
|
||||
* @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.
|
||||
@@ -397,7 +362,7 @@ public abstract class GodotPlugin {
|
||||
godot.runOnRenderThread(() -> nativeEmitSignal(pluginName, signalInfo.getName(), signalArgs));
|
||||
|
||||
} catch (IllegalArgumentException exception) {
|
||||
Log.w(TAG, exception.getMessage());
|
||||
Log.w(TAG, exception);
|
||||
if (BuildConfig.DEBUG) {
|
||||
throw exception;
|
||||
}
|
||||
@@ -420,13 +385,7 @@ public abstract class GodotPlugin {
|
||||
private static native void nativeRegisterMethod(String p_sname, String p_name, String p_ret, String[] p_params);
|
||||
|
||||
/**
|
||||
* Used to register gdextension libraries bundled by the plugin.
|
||||
* @param gdextensionPaths Paths to the libraries relative to the 'assets' directory.
|
||||
*/
|
||||
private static native void nativeRegisterGDExtensionLibraries(String[] gdextensionPaths);
|
||||
|
||||
/**
|
||||
* Used to complete registration of the {@link GodotPlugin} instance's methods.
|
||||
* Used to complete registration of the {@link GodotPlugin} instance's signals.
|
||||
* @param pluginName Name of the plugin
|
||||
* @param signalName Name of the signal to register
|
||||
* @param signalParamTypes Signal parameters types
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
/**************************************************************************/
|
||||
/* GodotPluginInfoProvider.java */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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.Collections;
|
||||
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 signals to be exposed to Godot.
|
||||
*/
|
||||
@NonNull
|
||||
default Set<SignalInfo> getPluginSignals() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the paths for the plugin's gdextension libraries (if any).
|
||||
*
|
||||
* The paths must be relative to the 'assets' directory and point to a '*.gdextension' file.
|
||||
*/
|
||||
@NonNull
|
||||
default Set<String> getPluginGDExtensionLibrariesPaths() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is invoked on the render thread when the plugin described by this instance has been
|
||||
* registered.
|
||||
*/
|
||||
default void onPluginRegistered() {
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,14 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
public final class GodotPluginRegistry {
|
||||
private static final String TAG = GodotPluginRegistry.class.getSimpleName();
|
||||
|
||||
/**
|
||||
* Prefix used for version 1 of the Godot plugin, compatible with Godot 3.x
|
||||
*/
|
||||
private static final String GODOT_PLUGIN_V1_NAME_PREFIX = "org.godotengine.plugin.v1.";
|
||||
/**
|
||||
* Prefix used for version 2 of the Godot plugin, compatible with Godot 4.x
|
||||
*/
|
||||
private static final String GODOT_PLUGIN_V2_NAME_PREFIX = "org.godotengine.plugin.v2.";
|
||||
|
||||
private static GodotPluginRegistry instance;
|
||||
private final ConcurrentHashMap<String, GodotPlugin> registry;
|
||||
@@ -123,12 +130,12 @@ public final class GodotPluginRegistry {
|
||||
return;
|
||||
}
|
||||
|
||||
int godotPluginV1NamePrefixLength = GODOT_PLUGIN_V1_NAME_PREFIX.length();
|
||||
int godotPluginV2NamePrefixLength = GODOT_PLUGIN_V2_NAME_PREFIX.length();
|
||||
for (String metaDataName : metaData.keySet()) {
|
||||
// Parse the meta-data looking for entry with the Godot plugin name prefix.
|
||||
if (metaDataName.startsWith(GODOT_PLUGIN_V1_NAME_PREFIX)) {
|
||||
String pluginName = metaDataName.substring(godotPluginV1NamePrefixLength).trim();
|
||||
Log.i(TAG, "Initializing Godot plugin " + pluginName);
|
||||
if (metaDataName.startsWith(GODOT_PLUGIN_V2_NAME_PREFIX)) {
|
||||
String pluginName = metaDataName.substring(godotPluginV2NamePrefixLength).trim();
|
||||
Log.i(TAG, "Initializing Godot v2 plugin " + pluginName);
|
||||
|
||||
// Retrieve the plugin class full name.
|
||||
String pluginHandleClassFullName = metaData.getString(metaDataName);
|
||||
@@ -148,25 +155,22 @@ public final class GodotPluginRegistry {
|
||||
"Meta-data plugin name does not match the value returned by the plugin handle: " + pluginName + " =/= " + pluginHandle.getPluginName());
|
||||
}
|
||||
registry.put(pluginName, pluginHandle);
|
||||
Log.i(TAG, "Completed initialization for Godot plugin " + pluginHandle.getPluginName());
|
||||
} catch (ClassNotFoundException e) {
|
||||
Log.w(TAG, "Unable to load Godot plugin " + pluginName, e);
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.w(TAG, "Unable to load Godot plugin " + pluginName, e);
|
||||
} catch (InstantiationException e) {
|
||||
Log.w(TAG, "Unable to load Godot plugin " + pluginName, e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
Log.w(TAG, "Unable to load Godot plugin " + pluginName, e);
|
||||
} catch (InvocationTargetException e) {
|
||||
Log.w(TAG, "Unable to load Godot plugin " + pluginName, e);
|
||||
Log.i(TAG, "Completed initialization for Godot v2 plugin " + pluginHandle.getPluginName());
|
||||
} catch (ClassNotFoundException | IllegalAccessException |
|
||||
InstantiationException | NoSuchMethodException |
|
||||
InvocationTargetException e) {
|
||||
Log.w(TAG, "Unable to load Godot v2 plugin " + pluginName, e);
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "Invalid plugin loader class for " + pluginName);
|
||||
}
|
||||
} else if (metaDataName.startsWith(GODOT_PLUGIN_V1_NAME_PREFIX)) {
|
||||
String v1PluginName = metaDataName.substring(GODOT_PLUGIN_V1_NAME_PREFIX.length()).trim();
|
||||
Log.w(TAG, "Godot 4 does not support Godot 3 (v1) plugin: " + v1PluginName);
|
||||
}
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.e(TAG, "Unable load Godot Android plugins from the manifest file.", e);
|
||||
Log.e(TAG, "Unable load Godot Android v2 plugins from the manifest file.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user