Skip to content

Commit 9de6978

Browse files
authored
Merge pull request godotengine#80740 from m4gr3d/godot_android_plugin_refactor_main
Godot Android plugin re-architecture
2 parents c05efbc + 6d982ad commit 9de6978

13 files changed

Lines changed: 169 additions & 207 deletions

File tree

core/extension/gdextension_manager.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ void GDExtensionManager::load_extensions() {
143143
ERR_CONTINUE_MSG(err == LOAD_STATUS_FAILED, "Error loading extension: " + s);
144144
}
145145
}
146+
147+
OS::get_singleton()->load_platform_gdextensions();
146148
}
147149

148150
GDExtensionManager *GDExtensionManager::get_singleton() {

core/os/os.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,10 @@ class OS {
328328

329329
virtual PreferredTextureFormat get_preferred_texture_format() const;
330330

331+
// Load GDExtensions specific to this platform.
332+
// This is invoked by the GDExtensionManager after loading GDExtensions specified by the project.
333+
virtual void load_platform_gdextensions() const {}
334+
331335
OS();
332336
virtual ~OS();
333337
};

editor/plugins/gdextension_export_plugin.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p
5050
Error err = config->load(p_path);
5151
ERR_FAIL_COND_MSG(err, "Failed to load GDExtension file: " + p_path);
5252

53+
// Check whether this GDExtension should be exported.
54+
bool android_aar_plugin = config->get_value("configuration", "android_aar_plugin", false);
55+
if (android_aar_plugin && p_features.has("android")) {
56+
// The gdextension configuration and Android .so files will be provided by the Android aar
57+
// plugin it's part of, so we abort here.
58+
skip();
59+
return;
60+
}
61+
5362
ERR_FAIL_COND_MSG(!config->has_section_key("configuration", "entry_symbol"), "Failed to export GDExtension file, missing entry symbol: " + p_path);
5463

5564
String entry_symbol = config->get_value("configuration", "entry_symbol");

platform/android/java/lib/src/org/godotengine/godot/Godot.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,19 @@ class Godot(private val context: Context) : SensorEventListener {
907907
return PermissionsUtil.getGrantedPermissions(getActivity())
908908
}
909909

910+
/**
911+
* Get the list of gdextension modules to register.
912+
*/
913+
@Keep
914+
private fun getGDExtensionConfigFiles(): Array<String> {
915+
val configFiles = mutableSetOf<String>()
916+
for (plugin in pluginRegistry.allPlugins) {
917+
configFiles.addAll(plugin.pluginGDExtensionLibrariesPaths)
918+
}
919+
920+
return configFiles.toTypedArray()
921+
}
922+
910923
@Keep
911924
private fun getCACertificates(): String {
912925
return GodotNetUtils.getCACertificates()

platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java

Lines changed: 50 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -57,27 +57,25 @@
5757
import javax.microedition.khronos.opengles.GL10;
5858

5959
/**
60-
* Base class for the Godot Android plugins.
60+
* Base class for Godot Android plugins.
6161
* <p>
62-
* A Godot Android plugin is a regular Android library packaged as an aar archive file with the following caveats:
62+
* A Godot Android plugin is an Android library with the following requirements:
6363
* <p>
64-
* - The library must have a dependency on the Godot Android library (godot-lib.aar).
65-
* A stable version is available for each release.
64+
* - The library must have a 'compileOnly' dependency on the Godot Android library: `compileOnly "org.godotengine:godot:<godotLibVersion>"`
6665
* <p>
67-
* - The library must include a <meta-data> tag in its manifest file setup as follow:
68-
* <meta-data android:name="org.godotengine.plugin.v1.[PluginName]" android:value="[plugin.init.ClassFullName]" />
66+
* - The library must include a <meta-data> tag in its Android manifest with the following format:
67+
* <meta-data android:name="org.godotengine.plugin.v2.[PluginName]" android:value="[plugin.init.ClassFullName]" />
6968
* Where:
7069
* - 'PluginName' is the name of the plugin.
71-
* - 'plugin.init.ClassFullName' is the full name (package + class name) of the plugin class
70+
* - 'plugin.init.ClassFullName' is the full name (package + class name) of the plugin init class
7271
* extending {@link GodotPlugin}.
72+
* <p>
73+
* A Godot Android plugin can also define and provide c/c++ gdextension libraries, which will be
74+
* automatically bundled by the aar build system.
75+
* GDExtension ('*.gdextension') config files must be located in the project 'assets' directory and
76+
* their paths specified by {@link GodotPlugin#getPluginGDExtensionLibrariesPaths()}.
7377
*
74-
* A plugin can also define and provide c/c++ gdextension libraries and nativescripts for the target
75-
* app/game to leverage.
76-
* The shared library for the gdextension library will be automatically bundled by the aar build
77-
* system.
78-
* Godot '*.gdextension' resource files must however be manually defined in the project
79-
* 'assets' directory. The recommended path for these resources in the 'assets' directory should be:
80-
* 'godot/plugin/v1/[PluginName]/'
78+
* @see <a href="https://docs.godotengine.org/en/stable/tutorials/platform/android/index.html">Android plugins</a>
8179
*/
8280
public abstract class GodotPlugin {
8381
private static final String TAG = GodotPlugin.class.getSimpleName();
@@ -97,7 +95,7 @@ protected Godot getGodot() {
9795
}
9896

9997
/**
100-
* Provides access to the underlying {@link Activity}.
98+
* Provides access to the hosting {@link Activity}.
10199
*/
102100
@Nullable
103101
protected Activity getActivity() {
@@ -106,33 +104,16 @@ protected Activity getActivity() {
106104

107105
/**
108106
* Register the plugin with Godot native code.
109-
*
110-
* This method is invoked on the render thread.
107+
* <p>
108+
* This method is invoked by the Godot Engine on the render thread.
111109
*/
112110
public final void onRegisterPluginWithGodotNative() {
113111
registeredSignals.putAll(
114-
registerPluginWithGodotNative(this, getPluginName(), getPluginMethods(), getPluginSignals(),
115-
getPluginGDExtensionLibrariesPaths()));
116-
}
117-
118-
/**
119-
* Register the plugin with Godot native code.
120-
*
121-
* This method must be invoked on the render thread.
122-
*/
123-
public static void registerPluginWithGodotNative(Object pluginObject,
124-
GodotPluginInfoProvider pluginInfoProvider) {
125-
registerPluginWithGodotNative(pluginObject, pluginInfoProvider.getPluginName(),
126-
Collections.emptyList(), pluginInfoProvider.getPluginSignals(),
127-
pluginInfoProvider.getPluginGDExtensionLibrariesPaths());
128-
129-
// Notify that registration is complete.
130-
pluginInfoProvider.onPluginRegistered();
112+
registerPluginWithGodotNative(this, getPluginName(), getPluginSignals()));
131113
}
132114

133115
private static Map<String, SignalInfo> registerPluginWithGodotNative(Object pluginObject,
134-
String pluginName, List<String> pluginMethods, Set<SignalInfo> pluginSignals,
135-
Set<String> pluginGDExtensionLibrariesPaths) {
116+
String pluginName, Set<SignalInfo> pluginSignals) {
136117
nativeRegisterSingleton(pluginName, pluginObject);
137118

138119
Set<Method> filteredMethods = new HashSet<>();
@@ -143,14 +124,6 @@ private static Map<String, SignalInfo> registerPluginWithGodotNative(Object plug
143124
// Check if the method is annotated with {@link UsedByGodot}.
144125
if (method.getAnnotation(UsedByGodot.class) != null) {
145126
filteredMethods.add(method);
146-
} else {
147-
// For backward compatibility, process the methods from the given <pluginMethods> argument.
148-
for (String methodName : pluginMethods) {
149-
if (methodName.equals(method.getName())) {
150-
filteredMethods.add(method);
151-
break;
152-
}
153-
}
154127
}
155128
}
156129

@@ -176,23 +149,18 @@ private static Map<String, SignalInfo> registerPluginWithGodotNative(Object plug
176149
registeredSignals.put(signalName, signalInfo);
177150
}
178151

179-
// Get the list of gdextension libraries to register.
180-
if (!pluginGDExtensionLibrariesPaths.isEmpty()) {
181-
nativeRegisterGDExtensionLibraries(pluginGDExtensionLibrariesPaths.toArray(new String[0]));
182-
}
183-
184152
return registeredSignals;
185153
}
186154

187155
/**
188-
* Invoked once during the Godot Android initialization process after creation of the
156+
* Invoked once during the initialization process after creation of the
189157
* {@link org.godotengine.godot.GodotRenderView} view.
190158
* <p>
191-
* The plugin can return a non-null {@link View} layout in order to add it to the Godot view
159+
* The plugin can return a non-null {@link View} layout which will be added to the Godot view
192160
* hierarchy.
193-
*
194-
* Use shouldBeOnTop() to set whether the plugin's {@link View} should be added on top or behind
195-
* the main Godot view.
161+
* <p>
162+
* Use {@link GodotPlugin#shouldBeOnTop()} to specify whether the plugin's {@link View} should
163+
* be added on top or behind the main Godot view.
196164
*
197165
* @see Activity#onCreate(Bundle)
198166
* @return the plugin's view to be included; null if no views should be included.
@@ -235,44 +203,52 @@ public void onMainDestroy() {}
235203
public boolean onMainBackPressed() { return false; }
236204

237205
/**
238-
* Invoked on the render thread when the Godot setup is complete.
206+
* Invoked on the render thread when set up of the Godot engine is complete.
207+
* <p>
208+
* This is invoked before {@link GodotPlugin#onGodotMainLoopStarted()}.
239209
*/
240210
public void onGodotSetupCompleted() {}
241211

242212
/**
243213
* Invoked on the render thread when the Godot main loop has started.
214+
*
215+
* This is invoked after {@link GodotPlugin#onGodotSetupCompleted()}.
244216
*/
245217
public void onGodotMainLoopStarted() {}
246218

247219
/**
248-
* Invoked once per frame on the GL thread after the frame is drawn.
220+
* When using the OpenGL renderer, this is invoked once per frame on the GL thread after the
221+
* frame is drawn.
249222
*/
250223
public void onGLDrawFrame(GL10 gl) {}
251224

252225
/**
253-
* Called on the GL thread after the surface is created and whenever the OpenGL ES surface size
254-
* changes.
226+
* When using the OpenGL renderer, this is called on the GL thread after the surface is created
227+
* and whenever the OpenGL ES surface size changes.
255228
*/
256229
public void onGLSurfaceChanged(GL10 gl, int width, int height) {}
257230

258231
/**
259-
* Called on the GL thread when the surface is created or recreated.
232+
* When using the OpenGL renderer, this is called on the GL thread when the surface is created
233+
* or recreated.
260234
*/
261235
public void onGLSurfaceCreated(GL10 gl, EGLConfig config) {}
262236

263237
/**
264-
* Invoked once per frame on the Vulkan thread after the frame is drawn.
238+
* When using the Vulkan renderer, this is invoked once per frame on the Vulkan thread after
239+
* the frame is drawn.
265240
*/
266241
public void onVkDrawFrame() {}
267242

268243
/**
269-
* Called on the Vulkan thread after the surface is created and whenever the surface size
270-
* changes.
244+
* When using the Vulkan renderer, this is called on the Vulkan thread after the surface is
245+
* created and whenever the surface size changes.
271246
*/
272247
public void onVkSurfaceChanged(Surface surface, int width, int height) {}
273248

274249
/**
275-
* Called on the Vulkan thread when the surface is created or recreated.
250+
* When using the Vulkan renderer, this is called on the Vulkan thread when the surface is
251+
* created or recreated.
276252
*/
277253
public void onVkSurfaceCreated(Surface surface) {}
278254

@@ -284,17 +260,6 @@ public void onVkSurfaceCreated(Surface surface) {}
284260
@NonNull
285261
public abstract String getPluginName();
286262

287-
/**
288-
* Returns the list of methods to be exposed to Godot.
289-
*
290-
* @deprecated Used the {@link UsedByGodot} annotation instead.
291-
*/
292-
@NonNull
293-
@Deprecated
294-
public List<String> getPluginMethods() {
295-
return Collections.emptyList();
296-
}
297-
298263
/**
299264
* Returns the list of signals to be exposed to Godot.
300265
*/
@@ -304,19 +269,19 @@ public Set<SignalInfo> getPluginSignals() {
304269
}
305270

306271
/**
307-
* Returns the paths for the plugin's gdextension libraries.
308-
*
309-
* The paths must be relative to the 'assets' directory and point to a '*.gdextension' file.
272+
* Returns the paths for the plugin's gdextension libraries (if any).
273+
* <p>
274+
* Each returned path must be relative to the 'assets' directory and point to a '*.gdextension' file.
310275
*/
311276
@NonNull
312-
protected Set<String> getPluginGDExtensionLibrariesPaths() {
277+
public Set<String> getPluginGDExtensionLibrariesPaths() {
313278
return Collections.emptySet();
314279
}
315280

316281
/**
317-
* Returns whether the plugin's {@link View} returned in onMainCreate() should be placed on
318-
* top of the main Godot view.
319-
*
282+
* Returns whether the plugin's {@link View} returned in
283+
* {@link GodotPlugin#onMainCreate(Activity)} should be placed on top of the main Godot view.
284+
* <p>
320285
* Returning false causes the plugin's {@link View} to be placed behind, which can be useful
321286
* when used with transparency in order to let the Godot view handle inputs.
322287
*/
@@ -359,7 +324,7 @@ protected void emitSignal(final String signalName, final Object... signalArgs) {
359324
}
360325
emitSignal(getGodot(), getPluginName(), signalInfo, signalArgs);
361326
} catch (IllegalArgumentException exception) {
362-
Log.w(TAG, exception.getMessage());
327+
Log.w(TAG, exception);
363328
if (BuildConfig.DEBUG) {
364329
throw exception;
365330
}
@@ -368,7 +333,7 @@ protected void emitSignal(final String signalName, final Object... signalArgs) {
368333

369334
/**
370335
* Emit a Godot signal.
371-
* @param godot
336+
* @param godot Godot instance
372337
* @param pluginName Name of the Godot plugin the signal will be emitted from. The plugin must already be registered with the Godot engine.
373338
* @param signalInfo Information about the signal to emit.
374339
* @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 static void emitSignal(Godot godot, String pluginName, SignalInfo signalI
397362
godot.runOnRenderThread(() -> nativeEmitSignal(pluginName, signalInfo.getName(), signalArgs));
398363

399364
} catch (IllegalArgumentException exception) {
400-
Log.w(TAG, exception.getMessage());
365+
Log.w(TAG, exception);
401366
if (BuildConfig.DEBUG) {
402367
throw exception;
403368
}
@@ -420,13 +385,7 @@ public static void emitSignal(Godot godot, String pluginName, SignalInfo signalI
420385
private static native void nativeRegisterMethod(String p_sname, String p_name, String p_ret, String[] p_params);
421386

422387
/**
423-
* Used to register gdextension libraries bundled by the plugin.
424-
* @param gdextensionPaths Paths to the libraries relative to the 'assets' directory.
425-
*/
426-
private static native void nativeRegisterGDExtensionLibraries(String[] gdextensionPaths);
427-
428-
/**
429-
* Used to complete registration of the {@link GodotPlugin} instance's methods.
388+
* Used to complete registration of the {@link GodotPlugin} instance's signals.
430389
* @param pluginName Name of the plugin
431390
* @param signalName Name of the signal to register
432391
* @param signalParamTypes Signal parameters types

0 commit comments

Comments
 (0)