Merge pull request #38175 from m4gr3d/improve_android_plugin_config
[3.2] Implementation of the Godot Android Plugin configuration file
This commit is contained in:
commit
a2f32c970d
|
@ -181,7 +181,7 @@
|
|||
<members>
|
||||
<member name="android/modules" type="String" setter="" getter="" default="""">
|
||||
Comma-separated list of custom Android modules (which must have been built in the Android export templates) using their Java package path, e.g. [code]"org/godotengine/godot/MyCustomSingleton,com/example/foo/FrenchFriesFactory"[/code].
|
||||
[b]Note:[/b] Since Godot 3.2.2, the [code]org/godotengine/godot/GodotPaymentV3[/code] module was deprecated and replaced by the [code]GodotPayment[/code] plugin which should be enabled in the Android export preset by adding [code]GodotPayment[/code] to the [code]custom_template/plugins[/code] option. The singleton to access in code was also renamed to [code]GodotPayment[/code].
|
||||
[b]Note:[/b] Since Godot 3.2.2, the [code]org/godotengine/godot/GodotPaymentV3[/code] module was deprecated and replaced by the [code]GodotPayment[/code] plugin which should be enabled in the Android export preset under [code]Plugins[/code] section. The singleton to access in code was also renamed to [code]GodotPayment[/code].
|
||||
</member>
|
||||
<member name="application/boot_splash/bg_color" type="Color" setter="" getter="" default="Color( 0.14, 0.14, 0.14, 1 )">
|
||||
Background color for the boot splash.
|
||||
|
|
|
@ -1224,6 +1224,8 @@ void EditorExport::save_presets() {
|
|||
void EditorExport::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method("_save", &EditorExport::_save);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("export_presets_updated"));
|
||||
}
|
||||
|
||||
void EditorExport::add_export_platform(const Ref<EditorExportPlatform> &p_platform) {
|
||||
|
@ -1311,8 +1313,13 @@ Vector<Ref<EditorExportPlugin> > EditorExport::get_export_plugins() {
|
|||
|
||||
void EditorExport::_notification(int p_what) {
|
||||
|
||||
if (p_what == NOTIFICATION_ENTER_TREE) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
load_config();
|
||||
} break;
|
||||
case NOTIFICATION_PROCESS: {
|
||||
update_export_presets();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1416,6 +1423,49 @@ void EditorExport::load_config() {
|
|||
block_save = false;
|
||||
}
|
||||
|
||||
void EditorExport::update_export_presets() {
|
||||
Map<StringName, List<EditorExportPlatform::ExportOption> > platform_options;
|
||||
|
||||
for (int i = 0; i < export_platforms.size(); i++) {
|
||||
Ref<EditorExportPlatform> platform = export_platforms[i];
|
||||
|
||||
if (platform->should_update_export_options()) {
|
||||
List<EditorExportPlatform::ExportOption> options;
|
||||
platform->get_export_options(&options);
|
||||
|
||||
platform_options[platform->get_name()] = options;
|
||||
}
|
||||
}
|
||||
|
||||
bool export_presets_updated = false;
|
||||
for (int i = 0; i < export_presets.size(); i++) {
|
||||
Ref<EditorExportPreset> preset = export_presets[i];
|
||||
if (platform_options.has(preset->get_platform()->get_name())) {
|
||||
export_presets_updated = true;
|
||||
|
||||
List<EditorExportPlatform::ExportOption> options = platform_options[preset->get_platform()->get_name()];
|
||||
|
||||
// Copy the previous preset values
|
||||
Map<StringName, Variant> previous_values = preset->values;
|
||||
|
||||
// Clear the preset properties and values prior to reloading
|
||||
preset->properties.clear();
|
||||
preset->values.clear();
|
||||
|
||||
for (List<EditorExportPlatform::ExportOption>::Element *E = options.front(); E; E = E->next()) {
|
||||
preset->properties.push_back(E->get().option);
|
||||
|
||||
StringName option_name = E->get().option.name;
|
||||
preset->values[option_name] = previous_values.has(option_name) ? previous_values[option_name] : E->get().default_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (export_presets_updated) {
|
||||
emit_signal(_export_presets_updated);
|
||||
}
|
||||
}
|
||||
|
||||
bool EditorExport::poll_export_platforms() {
|
||||
|
||||
bool changed = false;
|
||||
|
@ -1437,7 +1487,10 @@ EditorExport::EditorExport() {
|
|||
save_timer->connect("timeout", this, "_save");
|
||||
block_save = false;
|
||||
|
||||
_export_presets_updated = "export_presets_updated";
|
||||
|
||||
singleton = this;
|
||||
set_process(true);
|
||||
}
|
||||
|
||||
EditorExport::~EditorExport() {
|
||||
|
|
|
@ -232,6 +232,7 @@ public:
|
|||
virtual Ref<EditorExportPreset> create_preset();
|
||||
|
||||
virtual void get_export_options(List<ExportOption> *r_options) = 0;
|
||||
virtual bool should_update_export_options() { return false; }
|
||||
virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { return true; }
|
||||
|
||||
virtual String get_os_name() const = 0;
|
||||
|
@ -354,6 +355,8 @@ class EditorExport : public Node {
|
|||
Vector<Ref<EditorExportPreset> > export_presets;
|
||||
Vector<Ref<EditorExportPlugin> > export_plugins;
|
||||
|
||||
StringName _export_presets_updated;
|
||||
|
||||
Timer *save_timer;
|
||||
bool block_save;
|
||||
|
||||
|
@ -385,7 +388,7 @@ public:
|
|||
Vector<Ref<EditorExportPlugin> > get_export_plugins();
|
||||
|
||||
void load_config();
|
||||
|
||||
void update_export_presets();
|
||||
bool poll_export_platforms();
|
||||
|
||||
EditorExport();
|
||||
|
|
|
@ -131,6 +131,12 @@ void ProjectExportDialog::_add_preset(int p_platform) {
|
|||
_edit_preset(EditorExport::get_singleton()->get_export_preset_count() - 1);
|
||||
}
|
||||
|
||||
void ProjectExportDialog::_force_update_current_preset_parameters() {
|
||||
// Force the parameters section to refresh its UI.
|
||||
parameters->edit(nullptr);
|
||||
_update_current_preset();
|
||||
}
|
||||
|
||||
void ProjectExportDialog::_update_current_preset() {
|
||||
|
||||
_edit_preset(presets->get_current());
|
||||
|
@ -1057,6 +1063,7 @@ void ProjectExportDialog::_bind_methods() {
|
|||
ClassDB::bind_method("set_export_path", &ProjectExportDialog::set_export_path);
|
||||
ClassDB::bind_method("get_export_path", &ProjectExportDialog::get_export_path);
|
||||
ClassDB::bind_method("get_current_preset", &ProjectExportDialog::get_current_preset);
|
||||
ClassDB::bind_method("_force_update_current_preset_parameters", &ProjectExportDialog::_force_update_current_preset_parameters);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "export_path"), "set_export_path", "get_export_path");
|
||||
}
|
||||
|
@ -1138,6 +1145,7 @@ ProjectExportDialog::ProjectExportDialog() {
|
|||
parameters->set_name(TTR("Options"));
|
||||
parameters->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
parameters->connect("property_edited", this, "_update_parameters");
|
||||
EditorExport::get_singleton()->connect("export_presets_updated", this, "_force_update_current_preset_parameters");
|
||||
|
||||
// Resources export parameters.
|
||||
|
||||
|
|
|
@ -123,6 +123,7 @@ private:
|
|||
void _delete_preset_confirm();
|
||||
void _update_export_all();
|
||||
|
||||
void _force_update_current_preset_parameters();
|
||||
void _update_current_preset();
|
||||
void _update_presets();
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "platform/android/logo.gen.h"
|
||||
#include "platform/android/plugin/godot_plugin_config.h"
|
||||
#include "platform/android/run_icon.gen.h"
|
||||
|
||||
#include <string.h>
|
||||
|
@ -255,21 +256,49 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
|
|||
EditorProgress *ep;
|
||||
};
|
||||
|
||||
Vector<PluginConfig> plugins;
|
||||
volatile bool plugins_changed;
|
||||
Mutex *plugins_lock;
|
||||
Vector<Device> devices;
|
||||
volatile bool devices_changed;
|
||||
Mutex *device_lock;
|
||||
Thread *device_thread;
|
||||
Thread *check_for_changes_thread;
|
||||
volatile bool quit_request;
|
||||
|
||||
static void _device_poll_thread(void *ud) {
|
||||
|
||||
static void _check_for_changes_poll_thread(void *ud) {
|
||||
EditorExportPlatformAndroid *ea = (EditorExportPlatformAndroid *)ud;
|
||||
|
||||
while (!ea->quit_request) {
|
||||
// Check for plugins updates
|
||||
{
|
||||
// Nothing to do if we already know the plugins have changed.
|
||||
if (!ea->plugins_changed) {
|
||||
Vector<PluginConfig> loaded_plugins = get_plugins();
|
||||
|
||||
ea->plugins_lock->lock();
|
||||
|
||||
if (ea->plugins.size() != loaded_plugins.size()) {
|
||||
ea->plugins_changed = true;
|
||||
} else {
|
||||
for (int i = 0; i < ea->plugins.size(); i++) {
|
||||
if (ea->plugins[i].name != loaded_plugins[i].name) {
|
||||
ea->plugins_changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ea->plugins_changed) {
|
||||
ea->plugins = loaded_plugins;
|
||||
}
|
||||
|
||||
ea->plugins_lock->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// Check for devices updates
|
||||
String adb = EditorSettings::get_singleton()->get("export/android/adb");
|
||||
if (FileAccess::exists(adb)) {
|
||||
|
||||
String devices;
|
||||
List<String> args;
|
||||
args.push_back("devices");
|
||||
|
@ -282,8 +311,9 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
|
|||
|
||||
String d = ds[i];
|
||||
int dpos = d.find("device");
|
||||
if (dpos == -1)
|
||||
if (dpos == -1) {
|
||||
continue;
|
||||
}
|
||||
d = d.substr(0, dpos).strip_edges();
|
||||
ldevices.push_back(d);
|
||||
}
|
||||
|
@ -293,12 +323,10 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
|
|||
bool different = false;
|
||||
|
||||
if (ea->devices.size() != ldevices.size()) {
|
||||
|
||||
different = true;
|
||||
} else {
|
||||
|
||||
for (int i = 0; i < ea->devices.size(); i++) {
|
||||
|
||||
if (ea->devices[i].id != ldevices[i]) {
|
||||
different = true;
|
||||
break;
|
||||
|
@ -307,11 +335,9 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
|
|||
}
|
||||
|
||||
if (different) {
|
||||
|
||||
Vector<Device> ndevices;
|
||||
|
||||
for (int i = 0; i < ldevices.size(); i++) {
|
||||
|
||||
Device d;
|
||||
d.id = ldevices[i];
|
||||
for (int j = 0; j < ea->devices.size(); j++) {
|
||||
|
@ -588,6 +614,73 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
|
|||
return abis;
|
||||
}
|
||||
|
||||
/// List the gdap files in the directory specified by the p_path parameter.
|
||||
static Vector<String> list_gdap_files(const String &p_path) {
|
||||
Vector<String> dir_files;
|
||||
DirAccessRef da = DirAccess::open(p_path);
|
||||
if (da) {
|
||||
da->list_dir_begin();
|
||||
while (true) {
|
||||
String file = da->get_next();
|
||||
if (file == "") {
|
||||
break;
|
||||
}
|
||||
|
||||
if (da->current_is_dir() || da->current_is_hidden()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (file.ends_with(PLUGIN_CONFIG_EXT)) {
|
||||
dir_files.push_back(file);
|
||||
}
|
||||
}
|
||||
da->list_dir_end();
|
||||
}
|
||||
|
||||
return dir_files;
|
||||
}
|
||||
|
||||
static Vector<PluginConfig> get_plugins() {
|
||||
Vector<PluginConfig> loaded_plugins;
|
||||
|
||||
String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/plugins");
|
||||
|
||||
// Add the prebuilt plugins
|
||||
loaded_plugins.append_array(get_prebuilt_plugins(plugins_dir));
|
||||
|
||||
if (DirAccess::exists(plugins_dir)) {
|
||||
Vector<String> plugins_filenames = list_gdap_files(plugins_dir);
|
||||
|
||||
if (!plugins_filenames.empty()) {
|
||||
Ref<ConfigFile> config_file = memnew(ConfigFile);
|
||||
for (int i = 0; i < plugins_filenames.size(); i++) {
|
||||
PluginConfig config = load_plugin_config(config_file, plugins_dir.plus_file(plugins_filenames[i]));
|
||||
if (config.valid_config) {
|
||||
loaded_plugins.push_back(config);
|
||||
} else {
|
||||
print_error("Invalid plugin config file " + plugins_filenames[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return loaded_plugins;
|
||||
}
|
||||
|
||||
static Vector<PluginConfig> get_enabled_plugins(const Ref<EditorExportPreset> &p_presets) {
|
||||
Vector<PluginConfig> enabled_plugins;
|
||||
Vector<PluginConfig> all_plugins = get_plugins();
|
||||
for (int i = 0; i < all_plugins.size(); i++) {
|
||||
PluginConfig plugin = all_plugins[i];
|
||||
bool enabled = p_presets->get("plugins/" + plugin.name);
|
||||
if (enabled) {
|
||||
enabled_plugins.push_back(plugin);
|
||||
}
|
||||
}
|
||||
|
||||
return enabled_plugins;
|
||||
}
|
||||
|
||||
static Error store_in_apk(APKExportData *ed, const String &p_path, const Vector<uint8_t> &p_data, int compression_method = Z_DEFLATED) {
|
||||
zip_fileinfo zipfi = get_zip_fileinfo();
|
||||
zipOpenNewFileInZip(ed->apk,
|
||||
|
@ -692,7 +785,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
|
|||
|
||||
int xr_mode_index = p_preset->get("xr_features/xr_mode");
|
||||
|
||||
String plugins = p_preset->get("custom_template/plugins");
|
||||
String plugins_names = get_plugins_names(get_enabled_plugins(p_preset));
|
||||
|
||||
Vector<String> perms;
|
||||
|
||||
|
@ -862,9 +955,9 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
|
|||
}
|
||||
}
|
||||
|
||||
if (tname == "meta-data" && attrname == "value" && value == "custom_template_plugins_value") {
|
||||
if (tname == "meta-data" && attrname == "value" && value == "plugins_value" && !plugins_names.empty()) {
|
||||
// Update the meta-data 'android:value' attribute with the list of enabled plugins.
|
||||
string_table.write[attr_value] = plugins;
|
||||
string_table.write[attr_value] = plugins_names;
|
||||
}
|
||||
|
||||
iofs += 20;
|
||||
|
@ -1398,7 +1491,14 @@ public:
|
|||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "custom_template/use_custom_build"), false));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/plugins", PROPERTY_HINT_PLACEHOLDER_TEXT, "Plugin1,Plugin2,..."), ""));
|
||||
|
||||
Vector<PluginConfig> plugins_configs = get_plugins();
|
||||
for (int i = 0; i < plugins_configs.size(); i++) {
|
||||
print_verbose("Found Android plugin " + plugins_configs[i].name);
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "plugins/" + plugins_configs[i].name), false));
|
||||
}
|
||||
plugins_changed = false;
|
||||
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0"));
|
||||
|
@ -1454,6 +1554,15 @@ public:
|
|||
return logo;
|
||||
}
|
||||
|
||||
virtual bool should_update_export_options() {
|
||||
bool export_options_changed = plugins_changed;
|
||||
if (export_options_changed) {
|
||||
// don't clear unless we're reporting true, to avoid race
|
||||
plugins_changed = false;
|
||||
}
|
||||
return export_options_changed;
|
||||
}
|
||||
|
||||
virtual bool poll_export() {
|
||||
|
||||
bool dc = devices_changed;
|
||||
|
@ -1781,11 +1890,11 @@ public:
|
|||
// this check helps users to notice the change to ensure that they change their settings.
|
||||
String modules = ProjectSettings::get_singleton()->get("android/modules");
|
||||
if (modules.find("org/godotengine/godot/GodotPaymentV3") != -1) {
|
||||
String plugins = p_preset->get("custom_template/plugins");
|
||||
if (plugins.split(",", false).find("GodotPayment") == -1) {
|
||||
bool godot_payment_enabled = p_preset->get("plugins/" + GODOT_PAYMENT.name);
|
||||
if (!godot_payment_enabled) {
|
||||
valid = false;
|
||||
err += TTR("Invalid \"GodotPaymentV3\" module included in the \"android/modules\" project setting (changed in Godot 3.2.2).\n"
|
||||
"Replace it by the \"GodotPayment\" plugin, which should be listed in the \"custom_template/plugins\" preset option.\n"
|
||||
"Replace it by the \"GodotPayment\" plugin, which should be enabled in the \"Plugins\" preset section.\n"
|
||||
"Note that the singleton was also renamed from \"GodotPayments\" to \"GodotPayment\".");
|
||||
err += "\n";
|
||||
}
|
||||
|
@ -2123,18 +2232,22 @@ public:
|
|||
#endif
|
||||
|
||||
String build_path = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/build");
|
||||
String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/plugins");
|
||||
|
||||
build_command = build_path.plus_file(build_command);
|
||||
|
||||
String package_name = get_package_name(p_preset->get("package/unique_name"));
|
||||
String plugins = p_preset->get("custom_template/plugins");
|
||||
|
||||
Vector<PluginConfig> enabled_plugins = get_enabled_plugins(p_preset);
|
||||
String local_plugins_binaries = get_plugins_binaries(BINARY_TYPE_LOCAL, enabled_plugins);
|
||||
String remote_plugins_binaries = get_plugins_binaries(BINARY_TYPE_REMOTE, enabled_plugins);
|
||||
String custom_maven_repos = get_plugins_custom_maven_repos(enabled_plugins);
|
||||
|
||||
List<String> cmdline;
|
||||
cmdline.push_back("build");
|
||||
cmdline.push_back("-Pexport_package_name=" + package_name); // argument to specify the package name.
|
||||
cmdline.push_back("-Pcustom_template_plugins_dir=" + plugins_dir); // argument to specify the plugins directory.
|
||||
cmdline.push_back("-Pcustom_template_plugins=" + plugins); // argument to specify the list of plugins to enable.
|
||||
cmdline.push_back("-Pplugins_local_binaries=" + local_plugins_binaries); // argument to specify the list of plugins local dependencies.
|
||||
cmdline.push_back("-Pplugins_remote_binaries=" + remote_plugins_binaries); // argument to specify the list of plugins remote dependencies.
|
||||
cmdline.push_back("-Pplugins_maven_repos=" + custom_maven_repos); // argument to specify the list of custom maven repos for the plugins dependencies.
|
||||
cmdline.push_back("-p"); // argument to specify the start directory.
|
||||
cmdline.push_back(build_path); // start directory.
|
||||
/*{ used for debug
|
||||
|
@ -2661,15 +2774,19 @@ public:
|
|||
|
||||
device_lock = Mutex::create();
|
||||
devices_changed = true;
|
||||
|
||||
plugins_lock = Mutex::create();
|
||||
plugins_changed = true;
|
||||
quit_request = false;
|
||||
device_thread = Thread::create(_device_poll_thread, this);
|
||||
check_for_changes_thread = Thread::create(_check_for_changes_poll_thread, this);
|
||||
}
|
||||
|
||||
~EditorExportPlatformAndroid() {
|
||||
quit_request = true;
|
||||
Thread::wait_to_finish(device_thread);
|
||||
Thread::wait_to_finish(check_for_changes_thread);
|
||||
memdelete(plugins_lock);
|
||||
memdelete(device_lock);
|
||||
memdelete(device_thread);
|
||||
memdelete(check_for_changes_thread);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -39,8 +39,8 @@
|
|||
|
||||
<!-- Metadata populated at export time and used by Godot to figure out which plugins must be enabled. -->
|
||||
<meta-data
|
||||
android:name="custom_template_plugins"
|
||||
android:value="custom_template_plugins_value"/>
|
||||
android:name="plugins"
|
||||
android:value="plugins_value"/>
|
||||
|
||||
<activity
|
||||
android:name=".GodotApp"
|
||||
|
|
|
@ -30,6 +30,16 @@ allprojects {
|
|||
jcenter()
|
||||
//CHUNK_ALLPROJECTS_REPOSITORIES_BEGIN
|
||||
//CHUNK_ALLPROJECTS_REPOSITORIES_END
|
||||
|
||||
// Godot user plugins custom maven repos
|
||||
String[] mavenRepos = getGodotPluginsMavenRepos()
|
||||
if (mavenRepos != null && mavenRepos.size() > 0) {
|
||||
for (String repoUrl : mavenRepos) {
|
||||
maven {
|
||||
url repoUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,15 +59,18 @@ dependencies {
|
|||
releaseImplementation fileTree(dir: 'libs/release', include: ['*.jar', '*.aar'])
|
||||
}
|
||||
|
||||
// Godot prebuilt plugins
|
||||
implementation fileTree(dir: 'libs/plugins', include: ["GodotPayment*.aar"])
|
||||
// Godot user plugins remote dependencies
|
||||
String[] remoteDeps = getGodotPluginsRemoteBinaries()
|
||||
if (remoteDeps != null && remoteDeps.size() > 0) {
|
||||
for (String dep : remoteDeps) {
|
||||
implementation dep
|
||||
}
|
||||
}
|
||||
|
||||
// Godot user plugins dependencies
|
||||
String pluginsDir = getGodotPluginsDirectory()
|
||||
String[] pluginsBinaries = getGodotPluginsBinaries()
|
||||
if (pluginsDir != null && !pluginsDir.isEmpty() &&
|
||||
pluginsBinaries != null && pluginsBinaries.size() > 0) {
|
||||
implementation fileTree(dir: pluginsDir, include: pluginsBinaries)
|
||||
// Godot user plugins local dependencies
|
||||
String[] pluginsBinaries = getGodotPluginsLocalBinaries()
|
||||
if (pluginsBinaries != null && pluginsBinaries.size() > 0) {
|
||||
implementation files(pluginsBinaries)
|
||||
}
|
||||
|
||||
//CHUNK_DEPENDENCIES_BEGIN
|
||||
|
|
|
@ -28,39 +28,68 @@ ext.getExportPackageName = { ->
|
|||
return appId
|
||||
}
|
||||
|
||||
final String PLUGIN_VALUE_SEPARATOR_REGEX = "\\|"
|
||||
|
||||
/**
|
||||
* Parse the project properties for the 'custom_template_plugins' property and return
|
||||
* Parse the project properties for the 'plugins_maven_repos' property and return the list
|
||||
* of maven repos.
|
||||
*/
|
||||
ext.getGodotPluginsMavenRepos = { ->
|
||||
Set<String> mavenRepos = []
|
||||
|
||||
// Retrieve the list of maven repos.
|
||||
if (project.hasProperty("plugins_maven_repos")) {
|
||||
String mavenReposProperty = project.property("plugins_maven_repos")
|
||||
if (mavenReposProperty != null && !mavenReposProperty.trim().isEmpty()) {
|
||||
for (String mavenRepoUrl : mavenReposProperty.split(PLUGIN_VALUE_SEPARATOR_REGEX)) {
|
||||
mavenRepos += mavenRepoUrl.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mavenRepos
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the project properties for the 'plugins_remote_binaries' property and return
|
||||
* it for inclusion in the build dependencies.
|
||||
*/
|
||||
ext.getGodotPluginsRemoteBinaries = { ->
|
||||
Set<String> remoteDeps = []
|
||||
|
||||
// Retrieve the list of remote plugins binaries.
|
||||
if (project.hasProperty("plugins_remote_binaries")) {
|
||||
String remoteDepsList = project.property("plugins_remote_binaries")
|
||||
if (remoteDepsList != null && !remoteDepsList.trim().isEmpty()) {
|
||||
for (String dep: remoteDepsList.split(PLUGIN_VALUE_SEPARATOR_REGEX)) {
|
||||
remoteDeps += dep.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
return remoteDeps
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the project properties for the 'plugins_local_binaries' property and return
|
||||
* their binaries for inclusion in the build dependencies.
|
||||
*
|
||||
* The listed plugins must have their binaries in the project plugins directory.
|
||||
* Returns the prebuilt plugins if the 'plugins_local_binaries' property is unavailable.
|
||||
*/
|
||||
ext.getGodotPluginsBinaries = { ->
|
||||
String[] binDeps = []
|
||||
ext.getGodotPluginsLocalBinaries = { ->
|
||||
// Set the prebuilt plugins as default. If custom build is enabled,
|
||||
// the 'plugins_local_binaries' will be defined so we can use it instead.
|
||||
Set<String> binDeps = ["libs/plugins/GodotPayment.release.aar"]
|
||||
|
||||
// Retrieve the list of enabled plugins.
|
||||
if (project.hasProperty("custom_template_plugins")) {
|
||||
String pluginsList = project.property("custom_template_plugins")
|
||||
// Retrieve the list of local plugins binaries.
|
||||
if (project.hasProperty("plugins_local_binaries")) {
|
||||
binDeps.clear()
|
||||
String pluginsList = project.property("plugins_local_binaries")
|
||||
if (pluginsList != null && !pluginsList.trim().isEmpty()) {
|
||||
for (String plugin : pluginsList.split(",")) {
|
||||
binDeps += plugin.trim() + "*.aar"
|
||||
for (String plugin : pluginsList.split(PLUGIN_VALUE_SEPARATOR_REGEX)) {
|
||||
binDeps += plugin.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return binDeps
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the project properties for the 'custom_template_plugins_dir' property and return
|
||||
* its value.
|
||||
*
|
||||
* The returned value is the directory containing user plugins.
|
||||
*/
|
||||
ext.getGodotPluginsDirectory = { ->
|
||||
// The plugins directory is provided by the 'custom_template_plugins_dir' property.
|
||||
String pluginsDir = project.hasProperty("custom_template_plugins_dir")
|
||||
? project.property("custom_template_plugins_dir")
|
||||
: ""
|
||||
|
||||
return pluginsDir
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ task generateGodotTemplates(type: GradleBuild) {
|
|||
startParameter.excludedTaskNames += ":lib:" + getSconsTaskName(buildType)
|
||||
}
|
||||
|
||||
tasks = ["copyGodotPaymentPluginToAppModule"]
|
||||
tasks = []
|
||||
|
||||
// Only build the apks and aar files for which we have native shared libraries.
|
||||
for (String target : supportedTargets) {
|
||||
|
@ -161,6 +161,7 @@ task generateGodotTemplates(type: GradleBuild) {
|
|||
}
|
||||
}
|
||||
|
||||
dependsOn 'copyGodotPaymentPluginToAppModule'
|
||||
finalizedBy 'zipCustomBuild'
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,9 @@ public final class GodotPluginRegistry {
|
|||
/**
|
||||
* Name for the metadata containing the list of Godot plugins to enable.
|
||||
*/
|
||||
private static final String GODOT_ENABLED_PLUGINS_LABEL = "custom_template_plugins";
|
||||
private static final String GODOT_ENABLED_PLUGINS_LABEL = "plugins";
|
||||
|
||||
private static final String PLUGIN_VALUE_SEPARATOR_REGEX = "\\|";
|
||||
|
||||
private static GodotPluginRegistry instance;
|
||||
private final ConcurrentHashMap<String, GodotPlugin> registry;
|
||||
|
@ -129,13 +131,13 @@ public final class GodotPluginRegistry {
|
|||
}
|
||||
|
||||
// When using the Godot editor for building and exporting the apk, this is used to check
|
||||
// which plugins to enable since the custom build template may contain prebuilt plugins.
|
||||
// which plugins to enable.
|
||||
// When using a custom process to generate the apk, the metadata is not needed since
|
||||
// it's assumed that the developer is aware of the dependencies included in the apk.
|
||||
final Set<String> enabledPluginsSet;
|
||||
if (metaData.containsKey(GODOT_ENABLED_PLUGINS_LABEL)) {
|
||||
String enabledPlugins = metaData.getString(GODOT_ENABLED_PLUGINS_LABEL, "");
|
||||
String[] enabledPluginsList = enabledPlugins.split(",");
|
||||
String[] enabledPluginsList = enabledPlugins.split(PLUGIN_VALUE_SEPARATOR_REGEX);
|
||||
if (enabledPluginsList.length == 0) {
|
||||
// No plugins to enable. Aborting early.
|
||||
return;
|
||||
|
@ -159,6 +161,8 @@ public final class GodotPluginRegistry {
|
|||
continue;
|
||||
}
|
||||
|
||||
Log.i(TAG, "Initializing Godot plugin " + pluginName);
|
||||
|
||||
// Retrieve the plugin class full name.
|
||||
String pluginHandleClassFullName = metaData.getString(metaDataName);
|
||||
if (!TextUtils.isEmpty(pluginHandleClassFullName)) {
|
||||
|
@ -178,6 +182,7 @@ 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) {
|
||||
|
|
|
@ -0,0 +1,251 @@
|
|||
/*************************************************************************/
|
||||
/* godot_plugin_config.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef GODOT_PLUGIN_CONFIG_H
|
||||
#define GODOT_PLUGIN_CONFIG_H
|
||||
|
||||
#include "core/error_list.h"
|
||||
#include "core/io/config_file.h"
|
||||
#include "core/ustring.h"
|
||||
|
||||
static const char *PLUGIN_CONFIG_EXT = ".gdap";
|
||||
|
||||
static const char *CONFIG_SECTION = "config";
|
||||
static const char *CONFIG_NAME_KEY = "name";
|
||||
static const char *CONFIG_BINARY_TYPE_KEY = "binary_type";
|
||||
static const char *CONFIG_BINARY_KEY = "binary";
|
||||
|
||||
static const char *DEPENDENCIES_SECTION = "dependencies";
|
||||
static const char *DEPENDENCIES_LOCAL_KEY = "local";
|
||||
static const char *DEPENDENCIES_REMOTE_KEY = "remote";
|
||||
static const char *DEPENDENCIES_CUSTOM_MAVEN_REPOS_KEY = "custom_maven_repos";
|
||||
|
||||
static const char *BINARY_TYPE_LOCAL = "local";
|
||||
static const char *BINARY_TYPE_REMOTE = "remote";
|
||||
|
||||
static const char *PLUGIN_VALUE_SEPARATOR = "|";
|
||||
|
||||
/*
|
||||
The `config` section and fields are required and defined as follow:
|
||||
- **name**: name of the plugin
|
||||
- **binary_type**: can be either `local` or `remote`. The type affects the **binary** field
|
||||
- **binary**:
|
||||
- if **binary_type** is `local`, then this should be the filename of the plugin `aar` file in the `res://android/plugins` directory (e.g: `MyPlugin.aar`).
|
||||
- if **binary_type** is `remote`, then this should be a declaration for a remote gradle binary (e.g: "org.godot.example:my-plugin:0.0.0").
|
||||
|
||||
The `dependencies` section and fields are optional and defined as follow:
|
||||
- **local**: contains a list of local `.aar` binary files the plugin depends on. The local binary dependencies must also be located in the `res://android/plugins` directory.
|
||||
- **remote**: contains a list of remote binary gradle dependencies for the plugin.
|
||||
- **custom_maven_repos**: contains a list of urls specifying custom maven repos required for the plugin's dependencies.
|
||||
|
||||
See https://github.com/godotengine/godot/issues/38157#issuecomment-618773871
|
||||
*/
|
||||
struct PluginConfig {
|
||||
// Set to true when the config file is properly loaded.
|
||||
bool valid_config = false;
|
||||
|
||||
// Required config section
|
||||
String name;
|
||||
String binary_type;
|
||||
String binary;
|
||||
|
||||
// Optional dependencies section
|
||||
Vector<String> local_dependencies;
|
||||
Vector<String> remote_dependencies;
|
||||
Vector<String> custom_maven_repos;
|
||||
};
|
||||
|
||||
/*
|
||||
* Set of prebuilt plugins.
|
||||
*/
|
||||
static const PluginConfig GODOT_PAYMENT = {
|
||||
/*.valid_config =*/true,
|
||||
/*.name =*/"GodotPayment",
|
||||
/*.binary_type =*/"local",
|
||||
/*.binary =*/"res://android/build/libs/plugins/GodotPayment.release.aar",
|
||||
/*.local_dependencies =*/{},
|
||||
/*.remote_dependencies =*/{},
|
||||
/*.custom_maven_repos =*/{}
|
||||
};
|
||||
|
||||
static inline String resolve_local_dependency_path(String plugin_config_dir, String dependency_path) {
|
||||
String absolute_path;
|
||||
if (!dependency_path.empty()) {
|
||||
if (dependency_path.is_abs_path()) {
|
||||
absolute_path = ProjectSettings::get_singleton()->globalize_path(dependency_path);
|
||||
} else {
|
||||
absolute_path = plugin_config_dir.plus_file(dependency_path);
|
||||
}
|
||||
}
|
||||
|
||||
return absolute_path;
|
||||
}
|
||||
|
||||
static inline PluginConfig resolve_prebuilt_plugin(PluginConfig prebuilt_plugin, String plugin_config_dir) {
|
||||
PluginConfig resolved = prebuilt_plugin;
|
||||
resolved.binary = resolved.binary_type == BINARY_TYPE_LOCAL ? resolve_local_dependency_path(plugin_config_dir, prebuilt_plugin.binary) : prebuilt_plugin.binary;
|
||||
if (!prebuilt_plugin.local_dependencies.empty()) {
|
||||
resolved.local_dependencies.clear();
|
||||
for (int i = 0; i < prebuilt_plugin.local_dependencies.size(); i++) {
|
||||
resolved.local_dependencies.push_back(resolve_local_dependency_path(plugin_config_dir, prebuilt_plugin.local_dependencies[i]));
|
||||
}
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
static inline Vector<PluginConfig> get_prebuilt_plugins(String plugins_base_dir) {
|
||||
Vector<PluginConfig> prebuilt_plugins;
|
||||
prebuilt_plugins.push_back(resolve_prebuilt_plugin(GODOT_PAYMENT, plugins_base_dir));
|
||||
return prebuilt_plugins;
|
||||
}
|
||||
|
||||
static inline bool is_plugin_config_valid(PluginConfig plugin_config) {
|
||||
bool valid_name = !plugin_config.name.empty();
|
||||
bool valid_binary_type = plugin_config.binary_type == BINARY_TYPE_LOCAL ||
|
||||
plugin_config.binary_type == BINARY_TYPE_REMOTE;
|
||||
|
||||
bool valid_binary = false;
|
||||
if (valid_binary_type) {
|
||||
valid_binary = !plugin_config.binary.empty() &&
|
||||
(plugin_config.binary_type == BINARY_TYPE_REMOTE ||
|
||||
FileAccess::exists(plugin_config.binary));
|
||||
}
|
||||
|
||||
bool valid_local_dependencies = true;
|
||||
if (!plugin_config.local_dependencies.empty()) {
|
||||
for (int i = 0; i < plugin_config.local_dependencies.size(); i++) {
|
||||
if (!FileAccess::exists(plugin_config.local_dependencies[i])) {
|
||||
valid_local_dependencies = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return valid_name && valid_binary && valid_binary_type && valid_local_dependencies;
|
||||
}
|
||||
|
||||
static inline PluginConfig load_plugin_config(Ref<ConfigFile> config_file, const String &path) {
|
||||
PluginConfig plugin_config = {};
|
||||
|
||||
if (config_file.is_valid()) {
|
||||
Error err = config_file->load(path);
|
||||
if (err == OK) {
|
||||
String config_base_dir = path.get_base_dir();
|
||||
|
||||
plugin_config.name = config_file->get_value(CONFIG_SECTION, CONFIG_NAME_KEY, String());
|
||||
plugin_config.binary_type = config_file->get_value(CONFIG_SECTION, CONFIG_BINARY_TYPE_KEY, String());
|
||||
|
||||
String binary_path = config_file->get_value(CONFIG_SECTION, CONFIG_BINARY_KEY, String());
|
||||
plugin_config.binary = plugin_config.binary_type == BINARY_TYPE_LOCAL ? resolve_local_dependency_path(config_base_dir, binary_path) : binary_path;
|
||||
|
||||
if (config_file->has_section(DEPENDENCIES_SECTION)) {
|
||||
Vector<String> local_dependencies_paths = config_file->get_value(DEPENDENCIES_SECTION, DEPENDENCIES_LOCAL_KEY, Vector<String>());
|
||||
if (!local_dependencies_paths.empty()) {
|
||||
for (int i = 0; i < local_dependencies_paths.size(); i++) {
|
||||
plugin_config.local_dependencies.push_back(resolve_local_dependency_path(config_base_dir, local_dependencies_paths[i]));
|
||||
}
|
||||
}
|
||||
|
||||
plugin_config.remote_dependencies = config_file->get_value(DEPENDENCIES_SECTION, DEPENDENCIES_REMOTE_KEY, Vector<String>());
|
||||
plugin_config.custom_maven_repos = config_file->get_value(DEPENDENCIES_SECTION, DEPENDENCIES_CUSTOM_MAVEN_REPOS_KEY, Vector<String>());
|
||||
}
|
||||
|
||||
plugin_config.valid_config = is_plugin_config_valid(plugin_config);
|
||||
}
|
||||
}
|
||||
|
||||
return plugin_config;
|
||||
}
|
||||
|
||||
static inline String get_plugins_binaries(String binary_type, Vector<PluginConfig> plugins_configs) {
|
||||
String plugins_binaries;
|
||||
if (!plugins_configs.empty()) {
|
||||
Vector<String> binaries;
|
||||
for (int i = 0; i < plugins_configs.size(); i++) {
|
||||
PluginConfig config = plugins_configs[i];
|
||||
if (!config.valid_config) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (config.binary_type == binary_type) {
|
||||
binaries.push_back(config.binary);
|
||||
}
|
||||
|
||||
if (binary_type == BINARY_TYPE_LOCAL) {
|
||||
binaries.append_array(config.local_dependencies);
|
||||
}
|
||||
|
||||
if (binary_type == BINARY_TYPE_REMOTE) {
|
||||
binaries.append_array(config.remote_dependencies);
|
||||
}
|
||||
}
|
||||
|
||||
plugins_binaries = String(PLUGIN_VALUE_SEPARATOR).join(binaries);
|
||||
}
|
||||
|
||||
return plugins_binaries;
|
||||
}
|
||||
|
||||
static inline String get_plugins_custom_maven_repos(Vector<PluginConfig> plugins_configs) {
|
||||
String custom_maven_repos;
|
||||
if (!plugins_configs.empty()) {
|
||||
Vector<String> repos_urls;
|
||||
for (int i = 0; i < plugins_configs.size(); i++) {
|
||||
PluginConfig config = plugins_configs[i];
|
||||
if (!config.valid_config) {
|
||||
continue;
|
||||
}
|
||||
|
||||
repos_urls.append_array(config.custom_maven_repos);
|
||||
}
|
||||
|
||||
custom_maven_repos = String(PLUGIN_VALUE_SEPARATOR).join(repos_urls);
|
||||
}
|
||||
return custom_maven_repos;
|
||||
}
|
||||
|
||||
static inline String get_plugins_names(Vector<PluginConfig> plugins_configs) {
|
||||
String plugins_names;
|
||||
if (!plugins_configs.empty()) {
|
||||
Vector<String> names;
|
||||
for (int i = 0; i < plugins_configs.size(); i++) {
|
||||
PluginConfig config = plugins_configs[i];
|
||||
if (!config.valid_config) {
|
||||
continue;
|
||||
}
|
||||
|
||||
names.push_back(config.name);
|
||||
}
|
||||
plugins_names = String(PLUGIN_VALUE_SEPARATOR).join(names);
|
||||
}
|
||||
|
||||
return plugins_names;
|
||||
}
|
||||
|
||||
#endif // GODOT_PLUGIN_CONFIG_H
|
Loading…
Reference in New Issue