mirror of
				https://github.com/godotengine/godot.git
				synced 2025-10-31 13:41:03 +00:00 
			
		
		
		
	Implementation of the Godot Android Plugin configuration file
This commit is contained in:
		
							parent
							
								
									163687d17a
								
							
						
					
					
						commit
						14e6696c8e
					
				
					 11 changed files with 534 additions and 54 deletions
				
			
		|  | @ -1162,6 +1162,7 @@ void EditorExport::save_presets() { | |||
| } | ||||
| 
 | ||||
| void EditorExport::_bind_methods() { | ||||
| 	ADD_SIGNAL(MethodInfo("export_presets_updated")); | ||||
| } | ||||
| 
 | ||||
| void EditorExport::add_export_platform(const Ref<EditorExportPlatform> &p_platform) { | ||||
|  | @ -1229,8 +1230,13 @@ Vector<Ref<EditorExportPlugin>> EditorExport::get_export_plugins() { | |||
| } | ||||
| 
 | ||||
| void EditorExport::_notification(int p_what) { | ||||
| 	if (p_what == NOTIFICATION_ENTER_TREE) { | ||||
| 		load_config(); | ||||
| 	switch (p_what) { | ||||
| 		case NOTIFICATION_ENTER_TREE: { | ||||
| 			load_config(); | ||||
| 		} break; | ||||
| 		case NOTIFICATION_PROCESS: { | ||||
| 			update_export_presets(); | ||||
| 		} break; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -1332,6 +1338,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; | ||||
| 	for (int i = 0; i < export_platforms.size(); i++) { | ||||
|  | @ -1351,7 +1400,10 @@ EditorExport::EditorExport() { | |||
| 	save_timer->connect("timeout", callable_mp(this, &EditorExport::_save)); | ||||
| 	block_save = false; | ||||
| 
 | ||||
| 	_export_presets_updated = "export_presets_updated"; | ||||
| 
 | ||||
| 	singleton = this; | ||||
| 	set_process(true); | ||||
| } | ||||
| 
 | ||||
| EditorExport::~EditorExport() { | ||||
|  |  | |||
|  | @ -227,6 +227,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; | ||||
|  | @ -350,6 +351,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; | ||||
| 
 | ||||
|  | @ -381,7 +384,7 @@ public: | |||
| 	Vector<Ref<EditorExportPlugin>> get_export_plugins(); | ||||
| 
 | ||||
| 	void load_config(); | ||||
| 
 | ||||
| 	void update_export_presets(); | ||||
| 	bool poll_export_platforms(); | ||||
| 
 | ||||
| 	EditorExport(); | ||||
|  |  | |||
|  | @ -133,6 +133,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()); | ||||
| } | ||||
|  | @ -1101,6 +1107,7 @@ ProjectExportDialog::ProjectExportDialog() { | |||
| 	parameters->set_name(TTR("Options")); | ||||
| 	parameters->set_v_size_flags(Control::SIZE_EXPAND_FILL); | ||||
| 	parameters->connect("property_edited", callable_mp(this, &ProjectExportDialog::_update_parameters)); | ||||
| 	EditorExport::get_singleton()->connect("export_presets_updated", callable_mp(this, &ProjectExportDialog::_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> | ||||
|  | @ -252,16 +253,45 @@ 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(); | ||||
| 
 | ||||
| 					MutexLock lock(ea->plugins_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; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			// Check for devices updates
 | ||||
| 			String adb = EditorSettings::get_singleton()->get("export/android/adb"); | ||||
| 			if (FileAccess::exists(adb)) { | ||||
| 				String devices; | ||||
|  | @ -573,6 +603,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, | ||||
|  | @ -674,7 +771,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; | ||||
| 
 | ||||
|  | @ -829,9 +926,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; | ||||
|  | @ -1354,7 +1451,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")); | ||||
|  | @ -1409,6 +1513,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; | ||||
| 		if (dc) { | ||||
|  | @ -1755,18 +1868,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
 | ||||
|  | @ -2283,14 +2400,15 @@ public: | |||
| 		run_icon->create_from_image(img); | ||||
| 
 | ||||
| 		devices_changed = true; | ||||
| 		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); | ||||
| 		memdelete(device_thread); | ||||
| 		Thread::wait_to_finish(check_for_changes_thread); | ||||
| 		memdelete(check_for_changes_thread); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -32,8 +32,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" | ||||
|  |  | |||
|  | @ -21,6 +21,16 @@ allprojects { | |||
|         mavenCentral() | ||||
|         google() | ||||
|         jcenter() | ||||
| 
 | ||||
|         // Godot user plugins custom maven repos | ||||
|         String[] mavenRepos = getGodotPluginsMavenRepos() | ||||
|         if (mavenRepos != null && mavenRepos.size() > 0) { | ||||
|             for (String repoUrl : mavenRepos) { | ||||
|                 maven { | ||||
|                     url repoUrl | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -40,15 +50,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) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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' | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -58,7 +58,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; | ||||
|  | @ -128,13 +130,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; | ||||
|  | @ -158,6 +160,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)) { | ||||
|  | @ -177,6 +181,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) { | ||||
|  |  | |||
							
								
								
									
										251
									
								
								platform/android/plugin/godot_plugin_config.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								platform/android/plugin/godot_plugin_config.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Fredia Huya-Kouadio
						Fredia Huya-Kouadio