2
0
Эх сурвалжийг харах

Merge pull request #41340 from naithar/feature/pluggable-ios-modules-3.2

[iOS] [3.2] iOS Plugins
Rémi Verschelde 4 жил өмнө
parent
commit
0f5d475100
47 өөрчлөгдсөн 1445 нэмэгдсэн , 459 устгасан
  1. 2 77
      misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
  2. 18 0
      modules/arkit/arkit.gdip
  3. 2 2
      modules/arkit/arkit_module.cpp
  4. 1 1
      modules/arkit/arkit_module.h
  5. 1 11
      modules/camera/SCsub
  6. 1 1
      modules/camera/config.py
  7. 0 6
      modules/camera/register_types.cpp
  8. 15 0
      modules/camera_iphone/SCsub
  9. 18 0
      modules/camera_iphone/camera.gdip
  10. 0 0
      modules/camera_iphone/camera_ios.h
  11. 0 0
      modules/camera_iphone/camera_ios.mm
  12. 40 0
      modules/camera_iphone/camera_module.cpp
  13. 32 0
      modules/camera_iphone/camera_module.h
  14. 6 0
      modules/camera_iphone/config.py
  15. 15 0
      modules/gamecenter/SCsub
  16. 6 0
      modules/gamecenter/config.py
  17. 0 4
      modules/gamecenter/game_center.h
  18. 15 10
      modules/gamecenter/game_center.mm
  19. 35 0
      modules/gamecenter/game_center_delegate.h
  20. 45 0
      modules/gamecenter/game_center_delegate.mm
  21. 48 0
      modules/gamecenter/game_center_module.cpp
  22. 32 0
      modules/gamecenter/game_center_module.h
  23. 17 0
      modules/gamecenter/gamecenter.gdip
  24. 15 0
      modules/icloud/SCsub
  25. 6 0
      modules/icloud/config.py
  26. 17 0
      modules/icloud/icloud.gdip
  27. 0 4
      modules/icloud/icloud.h
  28. 2 6
      modules/icloud/icloud.mm
  29. 48 0
      modules/icloud/icloud_module.cpp
  30. 32 0
      modules/icloud/icloud_module.h
  31. 15 0
      modules/inappstore/SCsub
  32. 6 0
      modules/inappstore/config.py
  33. 0 4
      modules/inappstore/in_app_store.h
  34. 0 4
      modules/inappstore/in_app_store.mm
  35. 48 0
      modules/inappstore/in_app_store_module.cpp
  36. 32 0
      modules/inappstore/in_app_store_module.h
  37. 17 0
      modules/inappstore/inappstore.gdip
  38. 15 15
      platform/android/export/export.cpp
  39. 68 50
      platform/android/plugin/godot_plugin_config.h
  40. 0 3
      platform/iphone/SCsub
  41. 0 15
      platform/iphone/detect.py
  42. 486 192
      platform/iphone/export/export.cpp
  43. 3 12
      platform/iphone/os_iphone.h
  44. 2 32
      platform/iphone/os_iphone.mm
  45. 283 0
      platform/iphone/plugin/godot_plugin_config.h
  46. 1 2
      platform/iphone/view_controller.h
  47. 0 8
      platform/iphone/view_controller.mm

+ 2 - 77
misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj

@@ -146,81 +146,6 @@
 						DevelopmentTeam = $team_id;
 						ProvisioningStyle = Automatic;
 						SystemCapabilities = {
-							com.apple.AccessWiFi = {
-								enabled = $access_wifi;
-							};
-							com.apple.ApplePay = {
-								enabled = 0;
-							};
-							com.apple.ApplicationGroups.iOS = {
-								enabled = 0;
-							};
-							com.apple.AutoFillCredentialProvider = {
-								enabled = 0;
-							};
-							com.apple.BackgroundModes = {
-								enabled = 0;
-							};
-							com.apple.ClassKit = {
-								enabled = 0;
-							};
-							com.apple.DataProtection = {
-								enabled = 0;
-							};
-							com.apple.GameCenter.iOS = {
-								enabled = $game_center;
-							};
-							com.apple.HealthKit = {
-								enabled = 0;
-							};
-							com.apple.HomeKit = {
-								enabled = 0;
-							};
-							com.apple.HotspotConfiguration = {
-								enabled = 0;
-							};
-							com.apple.InAppPurchase = {
-								enabled = $in_app_purchases;
-							};
-							com.apple.InterAppAudio = {
-								enabled = 0;
-							};
-							com.apple.Keychain = {
-								enabled = 0;
-							};
-							com.apple.Maps.iOS = {
-								enabled = 0;
-							};
-							com.apple.Multipath = {
-								enabled = 0;
-							};
-							com.apple.NearFieldCommunicationTagReading = {
-								enabled = 0;
-							};
-							com.apple.NetworkExtensions.iOS = {
-								enabled = 0;
-							};
-							com.apple.Push = {
-								enabled = $push_notifications;
-							};
-							com.apple.SafariKeychain = {
-								enabled = 0;
-							};
-							com.apple.Siri = {
-								enabled = 0;
-							};
-							com.apple.VPNLite = {
-								enabled = 0;
-							};
-							com.apple.WAC = {
-								enabled = 0;
-							};
-							com.apple.Wallet = {
-								enabled = 0;
-							};
-							com.apple.iCloud = {
-								enabled = 0;
-							};
 						};
 					};
 				};
@@ -385,7 +310,7 @@
 				);
 				LIBRARY_SEARCH_PATHS = (
 					"$(inherited)",
-					"$(PROJECT_DIR)",
+					"$(PROJECT_DIR)/**",
 				);
 				PRODUCT_BUNDLE_IDENTIFIER = $identifier;
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -415,7 +340,7 @@
 				);
 				LIBRARY_SEARCH_PATHS = (
 					"$(inherited)",
-					"$(PROJECT_DIR)",
+					"$(PROJECT_DIR)/**",
 				);
 				PRODUCT_BUNDLE_IDENTIFIER = $identifier;
 				PRODUCT_NAME = "$(TARGET_NAME)";

+ 18 - 0
modules/arkit/arkit.gdip

@@ -0,0 +1,18 @@
+[config]
+name="ARKit"
+binary="arkit_lib.a"
+
+initialization="register_arkit_types"
+deinitialization="unregister_arkit_types"
+
+[dependencies]
+linked=[]
+embedded=[]
+system=["AVFoundation.framework", "ARKit.framework"]
+
+capabilities=["arkit"]
+
+files=[]
+
+[plist]
+NSCameraUsageDescription="Device camera is used for some functionality"

+ 2 - 2
modules/arkit/register_types.cpp → modules/arkit/arkit_module.cpp

@@ -1,5 +1,5 @@
 /*************************************************************************/
-/*  register_types.cpp                                                   */
+/*  arkit_module.cpp                                                     */
 /*************************************************************************/
 /*                       This file is part of:                           */
 /*                           GODOT ENGINE                                */
@@ -28,7 +28,7 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
 
-#include "register_types.h"
+#include "arkit_module.h"
 
 #include "arkit_interface.h"
 

+ 1 - 1
modules/arkit/register_types.h → modules/arkit/arkit_module.h

@@ -1,5 +1,5 @@
 /*************************************************************************/
-/*  register_types.h                                                     */
+/*  arkit_module.h                                                       */
 /*************************************************************************/
 /*                       This file is part of:                           */
 /*                           GODOT ENGINE                                */

+ 1 - 11
modules/camera/SCsub

@@ -5,17 +5,7 @@ Import("env_modules")
 
 env_camera = env_modules.Clone()
 
-if env["platform"] == "iphone":
-    # (iOS) Enable module support
-    env_camera.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
-
-    # (iOS) Build as separate static library
-    modules_sources = []
-    env_camera.add_source_files(modules_sources, "register_types.cpp")
-    env_camera.add_source_files(modules_sources, "camera_ios.mm")
-    mod_lib = env_modules.add_library("#bin/libgodot_camera_module" + env["LIBSUFFIX"], modules_sources)
-
-elif env["platform"] == "windows":
+if env["platform"] == "windows":
     env_camera.add_source_files(env.modules_sources, "register_types.cpp")
     env_camera.add_source_files(env.modules_sources, "camera_win.cpp")
 

+ 1 - 1
modules/camera/config.py

@@ -1,5 +1,5 @@
 def can_build(env, platform):
-    return platform == "iphone" or platform == "osx" or platform == "windows"
+    return platform == "osx" or platform == "windows"
 
 
 def configure(env):

+ 0 - 6
modules/camera/register_types.cpp

@@ -33,9 +33,6 @@
 #if defined(WINDOWS_ENABLED)
 #include "camera_win.h"
 #endif
-#if defined(IPHONE_ENABLED)
-#include "camera_ios.h"
-#endif
 #if defined(OSX_ENABLED)
 #include "camera_osx.h"
 #endif
@@ -44,9 +41,6 @@ void register_camera_types() {
 #if defined(WINDOWS_ENABLED)
 	CameraServer::make_default<CameraWindows>();
 #endif
-#if defined(IPHONE_ENABLED)
-	CameraServer::make_default<CameraIOS>();
-#endif
 #if defined(OSX_ENABLED)
 	CameraServer::make_default<CameraOSX>();
 #endif

+ 15 - 0
modules/camera_iphone/SCsub

@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_camera = env_modules.Clone()
+
+# (iOS) Enable module support
+env_camera.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
+
+# (iOS) Build as separate static library
+modules_sources = []
+env_camera.add_source_files(modules_sources, "*.cpp")
+env_camera.add_source_files(modules_sources, "*.mm")
+mod_lib = env_modules.add_library("#bin/libgodot_camera_module" + env["LIBSUFFIX"], modules_sources)

+ 18 - 0
modules/camera_iphone/camera.gdip

@@ -0,0 +1,18 @@
+[config]
+name="Camera"
+binary="camera_lib.a"
+
+initialization="register_camera_types"
+deinitialization="unregister_camera_types"
+
+[dependencies]
+linked=[]
+embedded=[]
+system=["AVFoundation.framework"]
+
+capabilities=[]
+
+files=[]
+
+[plist]
+NSCameraUsageDescription="Device camera is used for some functionality"

+ 0 - 0
modules/camera/camera_ios.h → modules/camera_iphone/camera_ios.h


+ 0 - 0
modules/camera/camera_ios.mm → modules/camera_iphone/camera_ios.mm


+ 40 - 0
modules/camera_iphone/camera_module.cpp

@@ -0,0 +1,40 @@
+/*************************************************************************/
+/*  camera_module.cpp                                                    */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "camera_module.h"
+
+#include "camera_ios.h"
+
+void register_camera_types() {
+	CameraServer::make_default<CameraIOS>();
+}
+
+void unregister_camera_types() {
+}

+ 32 - 0
modules/camera_iphone/camera_module.h

@@ -0,0 +1,32 @@
+/*************************************************************************/
+/*  camera_module.h                                                      */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+void register_camera_types();
+void unregister_camera_types();

+ 6 - 0
modules/camera_iphone/config.py

@@ -0,0 +1,6 @@
+def can_build(env, platform):
+    return platform == "iphone"
+
+
+def configure(env):
+    pass

+ 15 - 0
modules/gamecenter/SCsub

@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_gamecenter = env_modules.Clone()
+
+# (iOS) Enable module support
+env_gamecenter.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
+
+# (iOS) Build as separate static library
+modules_sources = []
+env_gamecenter.add_source_files(modules_sources, "*.cpp")
+env_gamecenter.add_source_files(modules_sources, "*.mm")
+mod_lib = env_modules.add_library("#bin/libgodot_gamecenter_module" + env["LIBSUFFIX"], modules_sources)

+ 6 - 0
modules/gamecenter/config.py

@@ -0,0 +1,6 @@
+def can_build(env, platform):
+    return platform == "iphone"
+
+
+def configure(env):
+    pass

+ 0 - 4
platform/iphone/game_center.h → modules/gamecenter/game_center.h

@@ -28,8 +28,6 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
 
-#ifdef GAME_CENTER_ENABLED
-
 #ifndef GAME_CENTER_H
 #define GAME_CENTER_H
 
@@ -72,5 +70,3 @@ public:
 };
 
 #endif
-
-#endif

+ 15 - 10
platform/iphone/game_center.mm → modules/gamecenter/game_center.mm

@@ -28,15 +28,16 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
 
-#ifdef GAME_CENTER_ENABLED
-
 #include "game_center.h"
 
-#import "app_delegate.h"
-#import "view_controller.h"
+#import "game_center_delegate.h"
+#import "platform/iphone/app_delegate.h"
+#import "platform/iphone/view_controller.h"
+
 #import <GameKit/GameKit.h>
 
 GameCenter *GameCenter::instance = NULL;
+GodotGameCenterDelegate *gameCenterDelegate = nil;
 
 void GameCenter::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("authenticate"), &GameCenter::authenticate);
@@ -64,7 +65,7 @@ Error GameCenter::authenticate() {
 	GKLocalPlayer *player = [GKLocalPlayer localPlayer];
 	ERR_FAIL_COND_V(![player respondsToSelector:@selector(authenticateHandler)], ERR_UNAVAILABLE);
 
-	ViewController *root_controller = (ViewController *)((AppDelegate *)[[UIApplication sharedApplication] delegate]).window.rootViewController;
+	UIViewController *root_controller = [[UIApplication sharedApplication] delegate].window.rootViewController;
 	ERR_FAIL_COND_V(!root_controller, FAILED);
 
 	// This handler is called several times.  First when the view needs to be shown, then again
@@ -298,10 +299,10 @@ Error GameCenter::show_game_center(Variant p_params) {
 	GKGameCenterViewController *controller = [[GKGameCenterViewController alloc] init];
 	ERR_FAIL_COND_V(!controller, FAILED);
 
-	ViewController *root_controller = (ViewController *)((AppDelegate *)[[UIApplication sharedApplication] delegate]).window.rootViewController;
+	UIViewController *root_controller = [[UIApplication sharedApplication] delegate].window.rootViewController;
 	ERR_FAIL_COND_V(!root_controller, FAILED);
 
-	controller.gameCenterDelegate = root_controller;
+	controller.gameCenterDelegate = gameCenterDelegate;
 	controller.viewState = view_state;
 	if (view_state == GKGameCenterViewControllerStateLeaderboards) {
 		controller.leaderboardIdentifier = nil;
@@ -373,8 +374,12 @@ GameCenter::GameCenter() {
 	ERR_FAIL_COND(instance != NULL);
 	instance = this;
 	authenticated = false;
-};
 
-GameCenter::~GameCenter(){};
+	gameCenterDelegate = [[GodotGameCenterDelegate alloc] init];
+};
 
-#endif
+GameCenter::~GameCenter() {
+	if (gameCenterDelegate) {
+		gameCenterDelegate = nil;
+	}
+}

+ 35 - 0
modules/gamecenter/game_center_delegate.h

@@ -0,0 +1,35 @@
+/*************************************************************************/
+/*  game_center_delegate.h                                               */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#import <GameKit/GameKit.h>
+
+@interface GodotGameCenterDelegate : NSObject <GKGameCenterControllerDelegate>
+
+@end

+ 45 - 0
modules/gamecenter/game_center_delegate.mm

@@ -0,0 +1,45 @@
+/*************************************************************************/
+/*  game_center_delegate.mm                                              */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#import "game_center_delegate.h"
+
+#include "game_center.h"
+
+@implementation GodotGameCenterDelegate
+
+- (void)gameCenterViewControllerDidFinish:(GKGameCenterViewController *)gameCenterViewController {
+	//[gameCenterViewController dismissViewControllerAnimated:YES completion:^{GameCenter::get_singleton()->game_center_closed();}];//version for signaling when overlay is completely gone
+	if (GameCenter::get_singleton()) {
+		GameCenter::get_singleton()->game_center_closed();
+	}
+	[gameCenterViewController dismissViewControllerAnimated:YES completion:nil];
+}
+
+@end

+ 48 - 0
modules/gamecenter/game_center_module.cpp

@@ -0,0 +1,48 @@
+/*************************************************************************/
+/*  game_center_module.cpp                                               */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "game_center_module.h"
+
+#include "core/engine.h"
+
+#include "game_center.h"
+
+GameCenter *game_center;
+
+void register_gamecenter_types() {
+	game_center = memnew(GameCenter);
+	Engine::get_singleton()->add_singleton(Engine::Singleton("GameCenter", game_center));
+}
+
+void unregister_gamecenter_types() {
+	if (game_center) {
+		memdelete(game_center);
+	}
+}

+ 32 - 0
modules/gamecenter/game_center_module.h

@@ -0,0 +1,32 @@
+/*************************************************************************/
+/*  game_center_module.h                                                 */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+void register_gamecenter_types();
+void unregister_gamecenter_types();

+ 17 - 0
modules/gamecenter/gamecenter.gdip

@@ -0,0 +1,17 @@
+[config]
+name="GameCenter"
+binary="gamecenter_lib.a"
+
+initialization="register_gamecenter_types"
+deinitialization="unregister_gamecenter_types"
+
+[dependencies]
+linked=[]
+embedded=[]
+system=["GameKit.framework"]
+
+capabilities=["gamekit"]
+
+files=[]
+
+[plist]

+ 15 - 0
modules/icloud/SCsub

@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_icloud = env_modules.Clone()
+
+# (iOS) Enable module support
+env_icloud.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
+
+# (iOS) Build as separate static library
+modules_sources = []
+env_icloud.add_source_files(modules_sources, "*.cpp")
+env_icloud.add_source_files(modules_sources, "*.mm")
+mod_lib = env_modules.add_library("#bin/libgodot_icloud_module" + env["LIBSUFFIX"], modules_sources)

+ 6 - 0
modules/icloud/config.py

@@ -0,0 +1,6 @@
+def can_build(env, platform):
+    return platform == "iphone"
+
+
+def configure(env):
+    pass

+ 17 - 0
modules/icloud/icloud.gdip

@@ -0,0 +1,17 @@
+[config]
+name="iCloud"
+binary="icloud_lib.a"
+
+initialization="register_icloud_types"
+deinitialization="unregister_icloud_types"
+
+[dependencies]
+linked=[]
+embedded=[]
+system=[]
+
+capabilities=[]
+
+files=[]
+
+[plist]

+ 0 - 4
platform/iphone/icloud.h → modules/icloud/icloud.h

@@ -28,8 +28,6 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
 
-#ifdef ICLOUD_ENABLED
-
 #ifndef ICLOUD_H
 #define ICLOUD_H
 
@@ -61,5 +59,3 @@ public:
 };
 
 #endif
-
-#endif

+ 2 - 6
platform/iphone/icloud.mm → modules/icloud/icloud.mm

@@ -28,11 +28,9 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
 
-#ifdef ICLOUD_ENABLED
-
 #include "icloud.h"
 
-#import "app_delegate.h"
+#import "platform/iphone/app_delegate.h"
 
 #import <Foundation/Foundation.h>
 
@@ -346,6 +344,4 @@ ICloud::ICloud() {
 					}];
 }
 
-ICloud::~ICloud(){};
-
-#endif
+ICloud::~ICloud() {}

+ 48 - 0
modules/icloud/icloud_module.cpp

@@ -0,0 +1,48 @@
+/*************************************************************************/
+/*  icloud_module.cpp                                                    */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "icloud_module.h"
+
+#include "core/engine.h"
+
+#include "icloud.h"
+
+ICloud *icloud;
+
+void register_icloud_types() {
+	icloud = memnew(ICloud);
+	Engine::get_singleton()->add_singleton(Engine::Singleton("ICloud", icloud));
+}
+
+void unregister_icloud_types() {
+	if (icloud) {
+		memdelete(icloud);
+	}
+}

+ 32 - 0
modules/icloud/icloud_module.h

@@ -0,0 +1,32 @@
+/*************************************************************************/
+/*  icloud_module.h                                                      */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+void register_icloud_types();
+void unregister_icloud_types();

+ 15 - 0
modules/inappstore/SCsub

@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_inappstore = env_modules.Clone()
+
+# (iOS) Enable module support
+env_inappstore.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
+
+# (iOS) Build as separate static library
+modules_sources = []
+env_inappstore.add_source_files(modules_sources, "*.cpp")
+env_inappstore.add_source_files(modules_sources, "*.mm")
+mod_lib = env_modules.add_library("#bin/libgodot_inappstore_module" + env["LIBSUFFIX"], modules_sources)

+ 6 - 0
modules/inappstore/config.py

@@ -0,0 +1,6 @@
+def can_build(env, platform):
+    return platform == "iphone"
+
+
+def configure(env):
+    pass

+ 0 - 4
platform/iphone/in_app_store.h → modules/inappstore/in_app_store.h

@@ -28,8 +28,6 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
 
-#ifdef STOREKIT_ENABLED
-
 #ifndef IN_APP_STORE_H
 #define IN_APP_STORE_H
 
@@ -78,5 +76,3 @@ public:
 };
 
 #endif
-
-#endif

+ 0 - 4
platform/iphone/in_app_store.mm → modules/inappstore/in_app_store.mm

@@ -28,8 +28,6 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
 
-#ifdef STOREKIT_ENABLED
-
 #include "in_app_store.h"
 
 #import <Foundation/Foundation.h>
@@ -414,5 +412,3 @@ InAppStore::~InAppStore() {
 	[[SKPaymentQueue defaultQueue] removeTransactionObserver:transactions_observer];
 	transactions_observer = nil;
 }
-
-#endif

+ 48 - 0
modules/inappstore/in_app_store_module.cpp

@@ -0,0 +1,48 @@
+/*************************************************************************/
+/*  in_app_store_module.cpp                                              */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "in_app_store_module.h"
+
+#include "core/engine.h"
+
+#include "in_app_store.h"
+
+InAppStore *store_kit;
+
+void register_inappstore_types() {
+	store_kit = memnew(InAppStore);
+	Engine::get_singleton()->add_singleton(Engine::Singleton("InAppStore", store_kit));
+}
+
+void unregister_inappstore_types() {
+	if (store_kit) {
+		memdelete(store_kit);
+	}
+}

+ 32 - 0
modules/inappstore/in_app_store_module.h

@@ -0,0 +1,32 @@
+/*************************************************************************/
+/*  in_app_store_module.h                                                */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+void register_inappstore_types();
+void unregister_inappstore_types();

+ 17 - 0
modules/inappstore/inappstore.gdip

@@ -0,0 +1,17 @@
+[config]
+name="InAppStore"
+binary="inappstore_lib.a"
+
+initialization="register_inappstore_types"
+deinitialization="unregister_inappstore_types"
+
+[dependencies]
+linked=[]
+embedded=[]
+system=["StoreKit.framework"]
+
+capabilities=[]
+
+files=[]
+
+[plist]

+ 15 - 15
platform/android/export/export.cpp

@@ -264,7 +264,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
 		EditorProgress *ep;
 	};
 
-	Vector<PluginConfig> plugins;
+	Vector<PluginConfigAndroid> plugins;
 	String last_plugin_names;
 	uint64_t last_custom_build_time = 0;
 	volatile bool plugins_changed;
@@ -283,7 +283,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
 			{
 				// Nothing to do if we already know the plugins have changed.
 				if (!ea->plugins_changed) {
-					Vector<PluginConfig> loaded_plugins = get_plugins();
+					Vector<PluginConfigAndroid> loaded_plugins = get_plugins();
 
 					ea->plugins_lock->lock();
 
@@ -640,7 +640,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
 					continue;
 				}
 
-				if (file.ends_with(PLUGIN_CONFIG_EXT)) {
+				if (file.ends_with(PluginConfigAndroid::PLUGIN_CONFIG_EXT)) {
 					dir_files.push_back(file);
 				}
 			}
@@ -650,8 +650,8 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
 		return dir_files;
 	}
 
-	static Vector<PluginConfig> get_plugins() {
-		Vector<PluginConfig> loaded_plugins;
+	static Vector<PluginConfigAndroid> get_plugins() {
+		Vector<PluginConfigAndroid> loaded_plugins;
 
 		String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/plugins");
 
@@ -664,7 +664,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
 			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]));
+					PluginConfigAndroid config = load_plugin_config(config_file, plugins_dir.plus_file(plugins_filenames[i]));
 					if (config.valid_config) {
 						loaded_plugins.push_back(config);
 					} else {
@@ -677,11 +677,11 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
 		return loaded_plugins;
 	}
 
-	static Vector<PluginConfig> get_enabled_plugins(const Ref<EditorExportPreset> &p_presets) {
-		Vector<PluginConfig> enabled_plugins;
-		Vector<PluginConfig> all_plugins = get_plugins();
+	static Vector<PluginConfigAndroid> get_enabled_plugins(const Ref<EditorExportPreset> &p_presets) {
+		Vector<PluginConfigAndroid> enabled_plugins;
+		Vector<PluginConfigAndroid> all_plugins = get_plugins();
 		for (int i = 0; i < all_plugins.size(); i++) {
-			PluginConfig plugin = all_plugins[i];
+			PluginConfigAndroid plugin = all_plugins[i];
 			bool enabled = p_presets->get("plugins/" + plugin.name);
 			if (enabled) {
 				enabled_plugins.push_back(plugin);
@@ -1655,7 +1655,7 @@ public:
 		r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "custom_template/use_custom_build"), false));
 		r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "custom_template/export_format", PROPERTY_HINT_ENUM, "Export APK,Export AAB"), EXPORT_FORMAT_APK));
 
-		Vector<PluginConfig> plugins_configs = get_plugins();
+		Vector<PluginConfigAndroid> 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));
@@ -2487,7 +2487,7 @@ public:
 		}
 	}
 
-	inline bool is_clean_build_required(Vector<PluginConfig> enabled_plugins) {
+	inline bool is_clean_build_required(Vector<PluginConfigAndroid> enabled_plugins) {
 		String plugin_names = get_plugins_names(enabled_plugins);
 		bool first_build = last_custom_build_time == 0;
 		bool have_plugins_changed = false;
@@ -2797,9 +2797,9 @@ public:
 			String sign_flag = should_sign ? "true" : "false";
 			String zipalign_flag = "true";
 
-			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);
+			Vector<PluginConfigAndroid> enabled_plugins = get_enabled_plugins(p_preset);
+			String local_plugins_binaries = get_plugins_binaries(PluginConfigAndroid::BINARY_TYPE_LOCAL, enabled_plugins);
+			String remote_plugins_binaries = get_plugins_binaries(PluginConfigAndroid::BINARY_TYPE_REMOTE, enabled_plugins);
 			String custom_maven_repos = get_plugins_custom_maven_repos(enabled_plugins);
 			bool clean_build_required = is_clean_build_required(enabled_plugins);
 

+ 68 - 50
platform/android/plugin/godot_plugin_config.h

@@ -35,23 +35,6 @@
 #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
@@ -67,7 +50,25 @@ The `dependencies` section and fields are optional and defined as follow:
 
  See https://github.com/godotengine/godot/issues/38157#issuecomment-618773871
  */
-struct PluginConfig {
+struct PluginConfigAndroid {
+
+	static const char *PLUGIN_CONFIG_EXT;
+
+	static const char *CONFIG_SECTION;
+	static const char *CONFIG_NAME_KEY;
+	static const char *CONFIG_BINARY_TYPE_KEY;
+	static const char *CONFIG_BINARY_KEY;
+
+	static const char *DEPENDENCIES_SECTION;
+	static const char *DEPENDENCIES_LOCAL_KEY;
+	static const char *DEPENDENCIES_REMOTE_KEY;
+	static const char *DEPENDENCIES_CUSTOM_MAVEN_REPOS_KEY;
+
+	static const char *BINARY_TYPE_LOCAL;
+	static const char *BINARY_TYPE_REMOTE;
+
+	static const char *PLUGIN_VALUE_SEPARATOR;
+
 	// Set to true when the config file is properly loaded.
 	bool valid_config = false;
 	// Unix timestamp of last change to this plugin.
@@ -84,11 +85,28 @@ struct PluginConfig {
 	Vector<String> custom_maven_repos;
 };
 
+const char *PluginConfigAndroid::PLUGIN_CONFIG_EXT = ".gdap";
+
+const char *PluginConfigAndroid::CONFIG_SECTION = "config";
+const char *PluginConfigAndroid::CONFIG_NAME_KEY = "name";
+const char *PluginConfigAndroid::CONFIG_BINARY_TYPE_KEY = "binary_type";
+const char *PluginConfigAndroid::CONFIG_BINARY_KEY = "binary";
+
+const char *PluginConfigAndroid::DEPENDENCIES_SECTION = "dependencies";
+const char *PluginConfigAndroid::DEPENDENCIES_LOCAL_KEY = "local";
+const char *PluginConfigAndroid::DEPENDENCIES_REMOTE_KEY = "remote";
+const char *PluginConfigAndroid::DEPENDENCIES_CUSTOM_MAVEN_REPOS_KEY = "custom_maven_repos";
+
+const char *PluginConfigAndroid::BINARY_TYPE_LOCAL = "local";
+const char *PluginConfigAndroid::BINARY_TYPE_REMOTE = "remote";
+
+const char *PluginConfigAndroid::PLUGIN_VALUE_SEPARATOR = "|";
+
 /*
  * Set of prebuilt plugins.
  * Currently unused, this is just for future reference:
  */
-// static const PluginConfig MY_PREBUILT_PLUGIN = {
+// static const PluginConfigAndroid MY_PREBUILT_PLUGIN = {
 //	/*.valid_config =*/true,
 //	/*.last_updated =*/0,
 //	/*.name =*/"GodotPayment",
@@ -112,9 +130,9 @@ static inline String resolve_local_dependency_path(String plugin_config_dir, Str
 	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;
+static inline PluginConfigAndroid resolve_prebuilt_plugin(PluginConfigAndroid prebuilt_plugin, String plugin_config_dir) {
+	PluginConfigAndroid resolved = prebuilt_plugin;
+	resolved.binary = resolved.binary_type == PluginConfigAndroid::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++) {
@@ -124,21 +142,21 @@ static inline PluginConfig resolve_prebuilt_plugin(PluginConfig prebuilt_plugin,
 	return resolved;
 }
 
-static inline Vector<PluginConfig> get_prebuilt_plugins(String plugins_base_dir) {
-	Vector<PluginConfig> prebuilt_plugins;
+static inline Vector<PluginConfigAndroid> get_prebuilt_plugins(String plugins_base_dir) {
+	Vector<PluginConfigAndroid> prebuilt_plugins;
 	// prebuilt_plugins.push_back(resolve_prebuilt_plugin(MY_PREBUILT_PLUGIN, plugins_base_dir));
 	return prebuilt_plugins;
 }
 
-static inline bool is_plugin_config_valid(PluginConfig plugin_config) {
+static inline bool is_plugin_config_valid(PluginConfigAndroid 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_type = plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_LOCAL ||
+							 plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_REMOTE;
 
 	bool valid_binary = false;
 	if (valid_binary_type) {
 		valid_binary = !plugin_config.binary.empty() &&
-					   (plugin_config.binary_type == BINARY_TYPE_REMOTE ||
+					   (plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_REMOTE ||
 							   FileAccess::exists(plugin_config.binary));
 	}
 
@@ -154,7 +172,7 @@ static inline bool is_plugin_config_valid(PluginConfig plugin_config) {
 	return valid_name && valid_binary && valid_binary_type && valid_local_dependencies;
 }
 
-static inline uint64_t get_plugin_modification_time(const PluginConfig &plugin_config, const String &config_path) {
+static inline uint64_t get_plugin_modification_time(const PluginConfigAndroid &plugin_config, const String &config_path) {
 	uint64_t last_updated = FileAccess::get_modified_time(config_path);
 	last_updated = MAX(last_updated, FileAccess::get_modified_time(plugin_config.binary));
 
@@ -166,30 +184,30 @@ static inline uint64_t get_plugin_modification_time(const PluginConfig &plugin_c
 	return last_updated;
 }
 
-static inline PluginConfig load_plugin_config(Ref<ConfigFile> config_file, const String &path) {
-	PluginConfig plugin_config = {};
+static inline PluginConfigAndroid load_plugin_config(Ref<ConfigFile> config_file, const String &path) {
+	PluginConfigAndroid 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());
+			plugin_config.name = config_file->get_value(PluginConfigAndroid::CONFIG_SECTION, PluginConfigAndroid::CONFIG_NAME_KEY, String());
+			plugin_config.binary_type = config_file->get_value(PluginConfigAndroid::CONFIG_SECTION, PluginConfigAndroid::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;
+			String binary_path = config_file->get_value(PluginConfigAndroid::CONFIG_SECTION, PluginConfigAndroid::CONFIG_BINARY_KEY, String());
+			plugin_config.binary = plugin_config.binary_type == PluginConfigAndroid::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 (config_file->has_section(PluginConfigAndroid::DEPENDENCIES_SECTION)) {
+				Vector<String> local_dependencies_paths = config_file->get_value(PluginConfigAndroid::DEPENDENCIES_SECTION, PluginConfigAndroid::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.remote_dependencies = config_file->get_value(PluginConfigAndroid::DEPENDENCIES_SECTION, PluginConfigAndroid::DEPENDENCIES_REMOTE_KEY, Vector<String>());
+				plugin_config.custom_maven_repos = config_file->get_value(PluginConfigAndroid::DEPENDENCIES_SECTION, PluginConfigAndroid::DEPENDENCIES_CUSTOM_MAVEN_REPOS_KEY, Vector<String>());
 			}
 
 			plugin_config.valid_config = is_plugin_config_valid(plugin_config);
@@ -200,12 +218,12 @@ static inline PluginConfig load_plugin_config(Ref<ConfigFile> config_file, const
 	return plugin_config;
 }
 
-static inline String get_plugins_binaries(String binary_type, Vector<PluginConfig> plugins_configs) {
+static inline String get_plugins_binaries(String binary_type, Vector<PluginConfigAndroid> 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];
+			PluginConfigAndroid config = plugins_configs[i];
 			if (!config.valid_config) {
 				continue;
 			}
@@ -214,27 +232,27 @@ static inline String get_plugins_binaries(String binary_type, Vector<PluginConfi
 				binaries.push_back(config.binary);
 			}
 
-			if (binary_type == BINARY_TYPE_LOCAL) {
+			if (binary_type == PluginConfigAndroid::BINARY_TYPE_LOCAL) {
 				binaries.append_array(config.local_dependencies);
 			}
 
-			if (binary_type == BINARY_TYPE_REMOTE) {
+			if (binary_type == PluginConfigAndroid::BINARY_TYPE_REMOTE) {
 				binaries.append_array(config.remote_dependencies);
 			}
 		}
 
-		plugins_binaries = String(PLUGIN_VALUE_SEPARATOR).join(binaries);
+		plugins_binaries = String(PluginConfigAndroid::PLUGIN_VALUE_SEPARATOR).join(binaries);
 	}
 
 	return plugins_binaries;
 }
 
-static inline String get_plugins_custom_maven_repos(Vector<PluginConfig> plugins_configs) {
+static inline String get_plugins_custom_maven_repos(Vector<PluginConfigAndroid> 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];
+			PluginConfigAndroid config = plugins_configs[i];
 			if (!config.valid_config) {
 				continue;
 			}
@@ -242,24 +260,24 @@ static inline String get_plugins_custom_maven_repos(Vector<PluginConfig> plugins
 			repos_urls.append_array(config.custom_maven_repos);
 		}
 
-		custom_maven_repos = String(PLUGIN_VALUE_SEPARATOR).join(repos_urls);
+		custom_maven_repos = String(PluginConfigAndroid::PLUGIN_VALUE_SEPARATOR).join(repos_urls);
 	}
 	return custom_maven_repos;
 }
 
-static inline String get_plugins_names(Vector<PluginConfig> plugins_configs) {
+static inline String get_plugins_names(Vector<PluginConfigAndroid> 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];
+			PluginConfigAndroid config = plugins_configs[i];
 			if (!config.valid_config) {
 				continue;
 			}
 
 			names.push_back(config.name);
 		}
-		plugins_names = String(PLUGIN_VALUE_SEPARATOR).join(names);
+		plugins_names = String(PluginConfigAndroid::PLUGIN_VALUE_SEPARATOR).join(names);
 	}
 
 	return plugins_names;

+ 0 - 3
platform/iphone/SCsub

@@ -9,9 +9,6 @@ iphone_lib = [
     "main.m",
     "app_delegate.mm",
     "view_controller.mm",
-    "game_center.mm",
-    "in_app_store.mm",
-    "icloud.mm",
     "ios.mm",
     "joypad_iphone.mm",
     "godot_view.mm",

+ 0 - 15
platform/iphone/detect.py

@@ -33,9 +33,6 @@ def get_opts():
             "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain",
         ),
         ("IPHONESDK", "Path to the iPhone SDK", ""),
-        BoolVariable("game_center", "Support for game center", True),
-        BoolVariable("store_kit", "Support for in-app store", True),
-        BoolVariable("icloud", "Support for iCloud", True),
         BoolVariable("ios_exceptions", "Enable exceptions", False),
         ("ios_triple", "Triple for ios toolchain", ""),
     ]
@@ -205,18 +202,6 @@ def configure(env):
         ]
     )
 
-    # Feature options
-    if env["game_center"]:
-        env.Append(CPPDEFINES=["GAME_CENTER_ENABLED"])
-        env.Append(LINKFLAGS=["-framework", "GameKit"])
-
-    if env["store_kit"]:
-        env.Append(CPPDEFINES=["STOREKIT_ENABLED"])
-        env.Append(LINKFLAGS=["-framework", "StoreKit"])
-
-    if env["icloud"]:
-        env.Append(CPPDEFINES=["ICLOUD_ENABLED"])
-
     env.Prepend(
         CPPPATH=[
             "$IPHONESDK/usr/include",

+ 486 - 192
platform/iphone/export/export.cpp

@@ -41,6 +41,7 @@
 #include "editor/editor_settings.h"
 #include "main/splash.gen.h"
 #include "platform/iphone/logo.gen.h"
+#include "platform/iphone/plugin/godot_plugin_config.h"
 #include "string.h"
 
 #include <sys/stat.h>
@@ -53,6 +54,13 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
 
 	Ref<ImageTexture> logo;
 
+	// Plugins
+	volatile bool plugins_changed;
+	Thread *check_for_changes_thread;
+	volatile bool quit_request;
+	Mutex *plugins_lock;
+	Vector<PluginConfigIOS> plugins;
+
 	typedef Error (*FileHandler)(String p_file, void *p_userdata);
 	static Error _walk_dir_recursive(DirAccess *p_da, FileHandler p_handler, void *p_userdata);
 	static Error _codesign(String p_file, void *p_userdata);
@@ -68,6 +76,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
 		String modules_fileref;
 		String modules_buildphase;
 		String modules_buildgrp;
+		Vector<String> capabilities;
 	};
 	struct ExportArchitecture {
 
@@ -103,8 +112,10 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
 	Vector<String> _get_preset_architectures(const Ref<EditorExportPreset> &p_preset);
 
 	void _add_assets_to_project(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets);
+	Error _copy_asset(const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets);
 	Error _export_additional_assets(const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets);
 	Error _export_additional_assets(const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets);
+	Error _export_ios_plugins(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &dest_dir, Vector<IOSExportAsset> &r_exported_assets, bool p_debug);
 
 	bool is_package_name_valid(const String &p_package, String *r_error = NULL) const {
 
@@ -130,6 +141,43 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
 		return true;
 	}
 
+	static void _check_for_changes_poll_thread(void *ud) {
+		EditorExportPlatformIOS *ea = (EditorExportPlatformIOS *)ud;
+
+		while (!ea->quit_request) {
+			// Nothing to do if we already know the plugins have changed.
+			if (!ea->plugins_changed) {
+
+				ea->plugins_lock->lock();
+
+				Vector<PluginConfigIOS> loaded_plugins = get_plugins();
+
+				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[i].last_updated != loaded_plugins[i].last_updated) {
+							ea->plugins_changed = true;
+							break;
+						}
+					}
+				}
+
+				ea->plugins_lock->unlock();
+			}
+
+			uint64_t wait = 3000000;
+			uint64_t time = OS::get_singleton()->get_ticks_usec();
+			while (OS::get_singleton()->get_ticks_usec() - time < wait) {
+				OS::get_singleton()->delay_usec(300000);
+
+				if (ea->quit_request) {
+					break;
+				}
+			}
+		}
+	}
+
 protected:
 	virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features);
 	virtual void get_export_options(List<ExportOption> *r_options);
@@ -139,13 +187,22 @@ public:
 	virtual String get_os_name() const { return "iOS"; }
 	virtual Ref<Texture> get_logo() const { 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 List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
 		List<String> list;
 		list.push_back("ipa");
 		return list;
 	}
+
 	virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
-	virtual void add_module_code(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &p_name, const String &p_fid, const String &p_gid);
 
 	virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const;
 
@@ -160,6 +217,85 @@ public:
 
 	EditorExportPlatformIOS();
 	~EditorExportPlatformIOS();
+
+	/// List the gdip files in the directory specified by the p_path parameter.
+	static Vector<String> list_plugin_config_files(const String &p_path, bool p_check_directories) {
+		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.empty()) {
+					break;
+				}
+
+				if (file == "." || file == "..") {
+					continue;
+				}
+
+				if (da->current_is_hidden()) {
+					continue;
+				}
+
+				if (da->current_is_dir()) {
+					if (p_check_directories) {
+						Vector<String> directory_files = list_plugin_config_files(p_path.plus_file(file), false);
+						for (int i = 0; i < directory_files.size(); ++i) {
+							dir_files.push_back(file.plus_file(directory_files[i]));
+						}
+					}
+
+					continue;
+				}
+
+				if (file.ends_with(PluginConfigIOS::PLUGIN_CONFIG_EXT)) {
+					dir_files.push_back(file);
+				}
+			}
+			da->list_dir_end();
+		}
+
+		return dir_files;
+	}
+
+	static Vector<PluginConfigIOS> get_plugins() {
+		Vector<PluginConfigIOS> loaded_plugins;
+
+		String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().plus_file("ios/plugins");
+
+		if (DirAccess::exists(plugins_dir)) {
+			Vector<String> plugins_filenames = list_plugin_config_files(plugins_dir, true);
+
+			if (!plugins_filenames.empty()) {
+				Ref<ConfigFile> config_file = memnew(ConfigFile);
+				for (int i = 0; i < plugins_filenames.size(); i++) {
+					PluginConfigIOS 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<PluginConfigIOS> get_enabled_plugins(const Ref<EditorExportPreset> &p_presets) {
+		Vector<PluginConfigIOS> enabled_plugins;
+		Vector<PluginConfigIOS> all_plugins = get_plugins();
+		for (int i = 0; i < all_plugins.size(); i++) {
+			PluginConfigIOS plugin = all_plugins[i];
+			bool enabled = p_presets->get("plugins/" + plugin.name);
+			if (enabled) {
+				enabled_plugins.push_back(plugin);
+			}
+		}
+
+		return enabled_plugins;
+	}
 };
 
 void EditorExportPlatformIOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) {
@@ -230,11 +366,15 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0"));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), ""));
 
-	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/arkit"), false));
-	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/camera"), false));
+	Vector<PluginConfigIOS> found_plugins = get_plugins();
+	for (int i = 0; i < found_plugins.size(); i++) {
+		r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "plugins/" + found_plugins[i].name), false));
+	}
+
+	plugins_changed = false;
+	plugins = found_plugins;
+
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/access_wifi"), false));
-	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/game_center"), true));
-	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/in_app_purchases"), false));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/push_notifications"), false));
 
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "user_data/accessible_from_files_app"), false));
@@ -341,18 +481,6 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
 			strnew += lines[i].replace("$docs_in_place", ((bool)p_preset->get("user_data/accessible_from_files_app")) ? "<true/>" : "<false/>") + "\n";
 		} else if (lines[i].find("$docs_sharing") != -1) {
 			strnew += lines[i].replace("$docs_sharing", ((bool)p_preset->get("user_data/accessible_from_itunes_sharing")) ? "<true/>" : "<false/>") + "\n";
-		} else if (lines[i].find("$access_wifi") != -1) {
-			bool is_on = p_preset->get("capabilities/access_wifi");
-			strnew += lines[i].replace("$access_wifi", is_on ? "1" : "0") + "\n";
-		} else if (lines[i].find("$game_center") != -1) {
-			bool is_on = p_preset->get("capabilities/game_center");
-			strnew += lines[i].replace("$game_center", is_on ? "1" : "0") + "\n";
-		} else if (lines[i].find("$in_app_purchases") != -1) {
-			bool is_on = p_preset->get("capabilities/in_app_purchases");
-			strnew += lines[i].replace("$in_app_purchases", is_on ? "1" : "0") + "\n";
-		} else if (lines[i].find("$push_notifications") != -1) {
-			bool is_on = p_preset->get("capabilities/push_notifications");
-			strnew += lines[i].replace("$push_notifications", is_on ? "1" : "0") + "\n";
 		} else if (lines[i].find("$entitlements_push_notifications") != -1) {
 			bool is_on = p_preset->get("capabilities/push_notifications");
 			strnew += lines[i].replace("$entitlements_push_notifications", is_on ? "<key>aps-environment</key><string>development</string>" : "") + "\n";
@@ -362,15 +490,14 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
 			// I've removed armv7 as we can run on 64bit only devices
 			// Note that capabilities listed here are requirements for the app to be installed.
 			// They don't enable anything.
+			Vector<String> capabilities_list = p_config.capabilities;
 
-			if ((bool)p_preset->get("capabilities/arkit")) {
-				capabilities += "<string>arkit</string>\n";
+			if ((bool)p_preset->get("capabilities/access_wifi") && capabilities_list.find("wifi") != -1) {
+				capabilities_list.push_back("wifi");
 			}
-			if ((bool)p_preset->get("capabilities/game_center")) {
-				capabilities += "<string>gamekit</string>\n";
-			}
-			if ((bool)p_preset->get("capabilities/access_wifi")) {
-				capabilities += "<string>wifi</string>\n";
+
+			for (int idx = 0; idx < capabilities_list.size(); idx++) {
+				capabilities += "<string>" + capabilities_list[idx] + "</string>\n";
 			}
 
 			strnew += lines[i].replace("$required_device_capabilities", capabilities);
@@ -850,28 +977,6 @@ void EditorExportPlatformIOS::_add_assets_to_project(const Ref<EditorExportPrese
 	// Note, frameworks like gamekit are always included in our project.pbxprof file
 	// even if turned off in capabilities.
 
-	// We do need our ARKit framework
-	if ((bool)p_preset->get("capabilities/arkit")) {
-		String build_id = (++current_id).str();
-		String ref_id = (++current_id).str();
-
-		if (pbx_frameworks_build.length() > 0) {
-			pbx_frameworks_build += ",\n";
-			pbx_frameworks_refs += ",\n";
-		}
-
-		pbx_frameworks_build += build_id;
-		pbx_frameworks_refs += ref_id;
-
-		Dictionary format_dict;
-		format_dict["build_id"] = build_id;
-		format_dict["ref_id"] = ref_id;
-		format_dict["name"] = "ARKit.framework";
-		format_dict["file_path"] = "System/Library/Frameworks/ARKit.framework";
-		format_dict["file_type"] = "wrapper.framework";
-		pbx_files += file_info_format.format(format_dict, "$_");
-	}
-
 	String str = String::utf8((const char *)p_project_data.ptr(), p_project_data.size());
 	str = str.replace("$additional_pbx_files", pbx_files);
 	str = str.replace("$additional_pbx_frameworks_build", pbx_frameworks_build);
@@ -887,142 +992,179 @@ void EditorExportPlatformIOS::_add_assets_to_project(const Ref<EditorExportPrese
 	}
 }
 
-Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets) {
+Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets) {
 	DirAccess *filesystem_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+	ERR_FAIL_COND_V_MSG(!filesystem_da, ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_out_dir + "'.");
+
 	String binary_name = p_out_dir.get_file().get_basename();
 
-	ERR_FAIL_COND_V_MSG(!filesystem_da, ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_out_dir + "'.");
-	for (int f_idx = 0; f_idx < p_assets.size(); ++f_idx) {
-		String asset = p_assets[f_idx];
-		if (!asset.begins_with("res://")) {
-			// either SDK-builtin or already a part of the export template
-			IOSExportAsset exported_asset = { asset, p_is_framework, p_should_embed };
-			r_exported_assets.push_back(exported_asset);
+	DirAccess *da = DirAccess::create_for_path(p_asset);
+	if (!da) {
+		memdelete(filesystem_da);
+		ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Can't create directory: " + p_asset + ".");
+	}
+	bool file_exists = da->file_exists(p_asset);
+	bool dir_exists = da->dir_exists(p_asset);
+	if (!file_exists && !dir_exists) {
+		memdelete(da);
+		memdelete(filesystem_da);
+		return ERR_FILE_NOT_FOUND;
+	}
+
+	String base_dir = p_asset.get_base_dir().replace("res://", "");
+	String destination_dir;
+	String destination;
+	String asset_path;
+
+	bool create_framework = false;
+
+	if (p_is_framework && p_asset.ends_with(".dylib")) {
+		// For iOS we need to turn .dylib into .framework
+		// to be able to send application to AppStore
+		asset_path = String("dylibs").plus_file(base_dir);
+
+		String file_name;
+
+		if (!p_custom_file_name) {
+			file_name = p_asset.get_basename().get_file();
 		} else {
-			DirAccess *da = DirAccess::create_for_path(asset);
-			if (!da) {
-				memdelete(filesystem_da);
-				ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Can't create directory: " + asset + ".");
-			}
-			bool file_exists = da->file_exists(asset);
-			bool dir_exists = da->dir_exists(asset);
-			if (!file_exists && !dir_exists) {
-				memdelete(da);
-				memdelete(filesystem_da);
-				return ERR_FILE_NOT_FOUND;
-			}
+			file_name = *p_custom_file_name;
+		}
 
-			String base_dir = asset.get_base_dir().replace("res://", "");
-			String destination_dir;
-			String destination;
-			String asset_path;
-
-			bool create_framework = false;
-
-			if (p_is_framework && asset.ends_with(".dylib")) {
-				// For iOS we need to turn .dylib into .framework
-				// to be able to send application to AppStore
-				asset_path = String("dylibs").plus_file(base_dir);
-
-				String file_name = asset.get_basename().get_file();
-				String framework_name = file_name + ".framework";
-
-				asset_path = asset_path.plus_file(framework_name);
-				destination_dir = p_out_dir.plus_file(asset_path);
-				destination = destination_dir.plus_file(file_name);
-				create_framework = true;
-			} else if (p_is_framework && (asset.ends_with(".framework") || asset.ends_with(".xcframework"))) {
-				asset_path = String("dylibs").plus_file(base_dir);
-
-				String file_name = asset.get_file();
-				asset_path = asset_path.plus_file(file_name);
-				destination_dir = p_out_dir.plus_file(asset_path);
-				destination = destination_dir;
-			} else {
-				asset_path = base_dir;
-
-				String file_name = asset.get_file();
-				destination_dir = p_out_dir.plus_file(asset_path);
-				asset_path = asset_path.plus_file(file_name);
-				destination = p_out_dir.plus_file(asset_path);
-			}
+		String framework_name = file_name + ".framework";
 
-			if (!filesystem_da->dir_exists(destination_dir)) {
-				Error make_dir_err = filesystem_da->make_dir_recursive(destination_dir);
-				if (make_dir_err) {
-					memdelete(da);
-					memdelete(filesystem_da);
-					return make_dir_err;
-				}
-			}
+		asset_path = asset_path.plus_file(framework_name);
+		destination_dir = p_out_dir.plus_file(asset_path);
+		destination = destination_dir.plus_file(file_name);
+		create_framework = true;
+	} else if (p_is_framework && (p_asset.ends_with(".framework") || p_asset.ends_with(".xcframework"))) {
+		asset_path = String("dylibs").plus_file(base_dir);
+
+		String file_name;
+
+		if (!p_custom_file_name) {
+			file_name = p_asset.get_file();
+		} else {
+			file_name = *p_custom_file_name;
+		}
+
+		asset_path = asset_path.plus_file(file_name);
+		destination_dir = p_out_dir.plus_file(asset_path);
+		destination = destination_dir;
+	} else {
+		asset_path = base_dir;
+
+		String file_name;
+
+		if (!p_custom_file_name) {
+			file_name = p_asset.get_file();
+		} else {
+			file_name = *p_custom_file_name;
+		}
+
+		destination_dir = p_out_dir.plus_file(asset_path);
+		asset_path = asset_path.plus_file(file_name);
+		destination = p_out_dir.plus_file(asset_path);
+	}
 
-			Error err = dir_exists ? da->copy_dir(asset, destination) : da->copy(asset, destination);
+	if (!filesystem_da->dir_exists(destination_dir)) {
+		Error make_dir_err = filesystem_da->make_dir_recursive(destination_dir);
+		if (make_dir_err) {
 			memdelete(da);
-			if (err) {
-				memdelete(filesystem_da);
-				return err;
-			}
-			IOSExportAsset exported_asset = { binary_name.plus_file(asset_path), p_is_framework, p_should_embed };
-			r_exported_assets.push_back(exported_asset);
+			memdelete(filesystem_da);
+			return make_dir_err;
+		}
+	}
 
-			if (create_framework) {
-				String file_name = asset.get_basename().get_file();
-				String framework_name = file_name + ".framework";
+	Error err = dir_exists ? da->copy_dir(p_asset, destination) : da->copy(p_asset, destination);
+	memdelete(da);
+	if (err) {
+		memdelete(filesystem_da);
+		return err;
+	}
+	IOSExportAsset exported_asset = { binary_name.plus_file(asset_path), p_is_framework, p_should_embed };
+	r_exported_assets.push_back(exported_asset);
 
-				// Performing `install_name_tool -id @rpath/{name}.framework/{name} ./{name}` on dylib
-				{
-					List<String> install_name_args;
-					install_name_args.push_back("-id");
-					install_name_args.push_back(String("@rpath").plus_file(framework_name).plus_file(file_name));
-					install_name_args.push_back(destination);
+	if (create_framework) {
+		String file_name;
 
-					OS::get_singleton()->execute("install_name_tool", install_name_args, true);
-				}
+		if (!p_custom_file_name) {
+			file_name = p_asset.get_basename().get_file();
+		} else {
+			file_name = *p_custom_file_name;
+		}
 
-				// Creating Info.plist
-				{
-					String info_plist_format = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
-											   "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
-											   "<plist version=\"1.0\">\n"
-											   "<dict>\n"
-											   "<key>CFBundleShortVersionString</key>\n"
-											   "<string>1.0</string>\n"
-											   "<key>CFBundleIdentifier</key>\n"
-											   "<string>com.gdnative.framework.$name</string>\n"
-											   "<key>CFBundleName</key>\n"
-											   "<string>$name</string>\n"
-											   "<key>CFBundleExecutable</key>\n"
-											   "<string>$name</string>\n"
-											   "<key>DTPlatformName</key>\n"
-											   "<string>iphoneos</string>\n"
-											   "<key>CFBundleInfoDictionaryVersion</key>\n"
-											   "<string>6.0</string>\n"
-											   "<key>CFBundleVersion</key>\n"
-											   "<string>1</string>\n"
-											   "<key>CFBundlePackageType</key>\n"
-											   "<string>FMWK</string>\n"
-											   "<key>MinimumOSVersion</key>\n"
-											   "<string>10.0</string>\n"
-											   "</dict>\n"
-											   "</plist>";
-
-					String info_plist = info_plist_format.replace("$name", file_name);
-
-					FileAccess *f = FileAccess::open(destination_dir.plus_file("Info.plist"), FileAccess::WRITE);
-					if (f) {
-						f->store_string(info_plist);
-						f->close();
-						memdelete(f);
-					}
-				}
+		String framework_name = file_name + ".framework";
+
+		// Performing `install_name_tool -id @rpath/{name}.framework/{name} ./{name}` on dylib
+		{
+			List<String> install_name_args;
+			install_name_args.push_back("-id");
+			install_name_args.push_back(String("@rpath").plus_file(framework_name).plus_file(file_name));
+			install_name_args.push_back(destination);
+
+			OS::get_singleton()->execute("install_name_tool", install_name_args, true);
+		}
+
+		// Creating Info.plist
+		{
+			String info_plist_format = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+									   "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
+									   "<plist version=\"1.0\">\n"
+									   "<dict>\n"
+									   "<key>CFBundleShortVersionString</key>\n"
+									   "<string>1.0</string>\n"
+									   "<key>CFBundleIdentifier</key>\n"
+									   "<string>com.gdnative.framework.$name</string>\n"
+									   "<key>CFBundleName</key>\n"
+									   "<string>$name</string>\n"
+									   "<key>CFBundleExecutable</key>\n"
+									   "<string>$name</string>\n"
+									   "<key>DTPlatformName</key>\n"
+									   "<string>iphoneos</string>\n"
+									   "<key>CFBundleInfoDictionaryVersion</key>\n"
+									   "<string>6.0</string>\n"
+									   "<key>CFBundleVersion</key>\n"
+									   "<string>1</string>\n"
+									   "<key>CFBundlePackageType</key>\n"
+									   "<string>FMWK</string>\n"
+									   "<key>MinimumOSVersion</key>\n"
+									   "<string>10.0</string>\n"
+									   "</dict>\n"
+									   "</plist>";
+
+			String info_plist = info_plist_format.replace("$name", file_name);
+
+			FileAccess *f = FileAccess::open(destination_dir.plus_file("Info.plist"), FileAccess::WRITE);
+			if (f) {
+				f->store_string(info_plist);
+				f->close();
+				memdelete(f);
 			}
 		}
 	}
+
 	memdelete(filesystem_da);
 
 	return OK;
 }
 
+Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets) {
+	for (int f_idx = 0; f_idx < p_assets.size(); ++f_idx) {
+		String asset = p_assets[f_idx];
+		if (!asset.begins_with("res://")) {
+			// either SDK-builtin or already a part of the export template
+			IOSExportAsset exported_asset = { asset, p_is_framework, p_should_embed };
+			r_exported_assets.push_back(exported_asset);
+		} else {
+			Error err = _copy_asset(p_out_dir, asset, nullptr, p_is_framework, p_should_embed, r_exported_assets);
+			ERR_FAIL_COND_V(err, err);
+		}
+	}
+
+	return OK;
+}
+
 Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets) {
 	Vector<Ref<EditorExportPlugin> > export_plugins = EditorExport::get_singleton()->get_export_plugins();
 	for (int i = 0; i < export_plugins.size(); i++) {
@@ -1067,20 +1209,173 @@ Vector<String> EditorExportPlatformIOS::_get_preset_architectures(const Ref<Edit
 	return enabled_archs;
 }
 
-void EditorExportPlatformIOS::add_module_code(const Ref<EditorExportPreset> &p_preset, EditorExportPlatformIOS::IOSConfigData &p_config_data, const String &p_name, const String &p_fid, const String &p_gid) {
-	if ((bool)p_preset->get("capabilities/" + p_name)) {
-		//add module static library
-		print_line("ADDING MODULE: " + p_name);
+Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &dest_dir, Vector<IOSExportAsset> &r_exported_assets, bool p_debug) {
+	String plugin_definition_cpp_code;
+	String plugin_initialization_cpp_code;
+	String plugin_deinitialization_cpp_code;
 
-		p_config_data.modules_buildfile += p_gid + " /* libgodot_" + p_name + "_module.a in Frameworks */ = {isa = PBXBuildFile; fileRef = " + p_fid + " /* libgodot_" + p_name + "_module.a */; };\n\t\t";
-		p_config_data.modules_fileref += p_fid + " /* libgodot_" + p_name + "_module.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = godot_" + p_name + "_module ; path = \"libgodot_" + p_name + "_module.a\"; sourceTree = \"<group>\"; };\n\t\t";
-		p_config_data.modules_buildphase += p_gid + " /* libgodot_" + p_name + "_module.a */,\n\t\t\t\t";
-		p_config_data.modules_buildgrp += p_fid + " /* libgodot_" + p_name + "_module.a */,\n\t\t\t\t";
-	} else {
-		//add stub function for disabled module
-		p_config_data.cpp_code += "void register_" + p_name + "_types() { /*stub*/ };\n";
-		p_config_data.cpp_code += "void unregister_" + p_name + "_types() { /*stub*/ };\n";
+	Vector<String> plugin_linked_dependencies;
+	Vector<String> plugin_embedded_dependencies;
+	Vector<String> plugin_files;
+
+	Vector<PluginConfigIOS> enabled_plugins = get_enabled_plugins(p_preset);
+
+	Vector<String> added_linked_dependenciy_names;
+	Vector<String> added_embedded_dependenciy_names;
+	HashMap<String, String> plist_values;
+
+	Error err;
+
+	for (int i = 0; i < enabled_plugins.size(); i++) {
+		PluginConfigIOS plugin = enabled_plugins[i];
+
+		// Export plugin binary.
+		if (!plugin.supports_targets) {
+			err = _copy_asset(dest_dir, plugin.binary, nullptr, true, true, r_exported_assets);
+		} else {
+			String plugin_binary_dir = plugin.binary.get_base_dir();
+			String plugin_name_prefix = plugin.binary.get_basename().get_file();
+			String plugin_file = plugin_name_prefix + "." + (p_debug ? "debug" : "release") + ".a";
+			String result_file_name = plugin.binary.get_file();
+
+			err = _copy_asset(dest_dir, plugin_binary_dir.plus_file(plugin_file), &result_file_name, true, true, r_exported_assets);
+		}
+
+		ERR_FAIL_COND_V(err, err);
+
+		// Adding dependencies.
+		// Use separate container for names to check for duplicates.
+		for (int j = 0; j < plugin.linked_dependencies.size(); j++) {
+			String dependency = plugin.linked_dependencies[j];
+			String name = dependency.get_file();
+
+			if (added_linked_dependenciy_names.find(name) != -1) {
+				continue;
+			}
+
+			added_linked_dependenciy_names.push_back(name);
+			plugin_linked_dependencies.push_back(dependency);
+		}
+
+		for (int j = 0; j < plugin.system_dependencies.size(); j++) {
+			String dependency = plugin.system_dependencies[j];
+			String name = dependency.get_file();
+
+			if (added_linked_dependenciy_names.find(name) != -1) {
+				continue;
+			}
+
+			added_linked_dependenciy_names.push_back(name);
+			plugin_linked_dependencies.push_back(dependency);
+		}
+
+		for (int j = 0; j < plugin.embedded_dependencies.size(); j++) {
+			String dependency = plugin.embedded_dependencies[j];
+			String name = dependency.get_file();
+
+			if (added_embedded_dependenciy_names.find(name) != -1) {
+				continue;
+			}
+
+			added_embedded_dependenciy_names.push_back(name);
+			plugin_embedded_dependencies.push_back(dependency);
+		}
+
+		plugin_files.append_array(plugin.files_to_copy);
+
+		// Capabilities
+		// Also checking for duplicates.
+		for (int j = 0; j < plugin.capabilities.size(); j++) {
+			String capability = plugin.capabilities[j];
+
+			if (p_config_data.capabilities.find(capability) != -1) {
+				continue;
+			}
+
+			p_config_data.capabilities.push_back(capability);
+		}
+
+		// Plist
+		// Using hash map container to remove duplicates
+		const String *K = nullptr;
+
+		while ((K = plugin.plist.next(K))) {
+			String key = *K;
+			String value = plugin.plist[key];
+
+			if (key.empty() || value.empty()) {
+				continue;
+			}
+
+			plist_values[key] = value;
+		}
+
+		// CPP Code
+		String definition_comment = "// Plugin: " + plugin.name + "\n";
+		String initialization_method = plugin.initialization_method + "();\n";
+		String deinitialization_method = plugin.deinitialization_method + "();\n";
+
+		plugin_definition_cpp_code += definition_comment +
+									  "extern void " + initialization_method +
+									  "extern void " + deinitialization_method + "\n";
+
+		plugin_initialization_cpp_code += "\t" + initialization_method;
+		plugin_deinitialization_cpp_code += "\t" + deinitialization_method;
+	}
+
+	// Updating `Info.plist`
+	{
+		const String *K = nullptr;
+		while ((K = plist_values.next(K))) {
+			String key = *K;
+			String value = plist_values[key];
+
+			if (key.empty() || value.empty()) {
+				continue;
+			}
+
+			p_config_data.plist_content += "<key>" + key + "</key><string>" + value + "</string>\n";
+		}
 	}
+
+	// Export files
+	{
+		// Export linked plugin dependency
+		err = _export_additional_assets(dest_dir, plugin_linked_dependencies, true, false, r_exported_assets);
+		ERR_FAIL_COND_V(err, err);
+
+		// Export embedded plugin dependency
+		err = _export_additional_assets(dest_dir, plugin_embedded_dependencies, true, true, r_exported_assets);
+		ERR_FAIL_COND_V(err, err);
+
+		// Export plugin files
+		err = _export_additional_assets(dest_dir, plugin_files, false, false, r_exported_assets);
+		ERR_FAIL_COND_V(err, err);
+	}
+
+	// Update CPP
+	{
+		Dictionary plugin_format;
+		plugin_format["definition"] = plugin_definition_cpp_code;
+		plugin_format["initialization"] = plugin_initialization_cpp_code;
+		plugin_format["deinitialization"] = plugin_deinitialization_cpp_code;
+
+		String plugin_cpp_code = "\n// Godot Plugins\n"
+								 "void godot_ios_plugins_initialize();\n"
+								 "void godot_ios_plugins_deinitialize();\n"
+								 "// Exported Plugins\n\n"
+								 "$definition"
+								 "// Use Plugins\n"
+								 "void godot_ios_plugins_initialize() {\n"
+								 "$initialization"
+								 "}\n\n"
+								 "void godot_ios_plugins_deinitialize() {\n"
+								 "$deinitialization"
+								 "}\n";
+
+		p_config_data.cpp_code += plugin_cpp_code.format(plugin_format, "$_");
+	}
+	return OK;
 }
 
 Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
@@ -1186,9 +1481,12 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
 		"",
 		"",
 		"",
-		""
+		"",
+		Vector<String>()
 	};
 
+	Vector<IOSExportAsset> assets;
+
 	DirAccess *tmp_app_path = DirAccess::create_for_path(dest_dir);
 	ERR_FAIL_COND_V(!tmp_app_path, ERR_CANT_CREATE);
 
@@ -1201,8 +1499,8 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
 		return ERR_CANT_OPEN;
 	}
 
-	add_module_code(p_preset, config_data, "arkit", "F9B95E6E2391205500AF0000", "F9C95E812391205C00BF0000");
-	add_module_code(p_preset, config_data, "camera", "F9B95E6E2391205500AF0001", "F9C95E812391205C00BF0001");
+	err = _export_ios_plugins(p_preset, config_data, dest_dir + binary_name, assets, p_debug);
+	ERR_FAIL_COND_V(err, err);
 
 	//export rest of the files
 	int ret = unzGoToFirstFile(src_pkg_zip);
@@ -1244,21 +1542,8 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
 			is_execute = true;
 #endif
 			file = "godot_ios.a";
-		} else if (file.begins_with("libgodot_arkit")) {
-			if ((bool)p_preset->get("capabilities/arkit") && file.ends_with(String(p_debug ? "debug" : "release") + ".fat.a")) {
-				file = "libgodot_arkit_module.a";
-			} else {
-				ret = unzGoToNextFile(src_pkg_zip);
-				continue; //ignore!
-			}
-		} else if (file.begins_with("libgodot_camera")) {
-			if ((bool)p_preset->get("capabilities/camera") && file.ends_with(String(p_debug ? "debug" : "release") + ".fat.a")) {
-				file = "libgodot_camera_module.a";
-			} else {
-				ret = unzGoToNextFile(src_pkg_zip);
-				continue; //ignore!
-			}
 		}
+
 		if (file == project_file) {
 			project_file_data = data;
 		}
@@ -1390,7 +1675,6 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
 	}
 
 	print_line("Exporting additional assets");
-	Vector<IOSExportAsset> assets;
 	_export_additional_assets(dest_dir + binary_name, libraries, assets);
 	_add_assets_to_project(p_preset, project_file_data, assets);
 	String project_file_name = dest_dir + binary_name + ".xcodeproj/project.pbxproj";
@@ -1526,9 +1810,19 @@ EditorExportPlatformIOS::EditorExportPlatformIOS() {
 	Ref<Image> img = memnew(Image(_iphone_logo));
 	logo.instance();
 	logo->create_from_image(img);
+
+	plugins_changed = true;
+	quit_request = false;
+	plugins_lock = Mutex::create();
+
+	check_for_changes_thread = Thread::create(_check_for_changes_poll_thread, this);
 }
 
 EditorExportPlatformIOS::~EditorExportPlatformIOS() {
+	quit_request = true;
+	Thread::wait_to_finish(check_for_changes_thread);
+	memdelete(plugins_lock);
+	memdelete(check_for_changes_thread);
 }
 
 void register_iphone_exporter() {

+ 3 - 12
platform/iphone/os_iphone.h

@@ -38,15 +38,15 @@
 #include "drivers/unix/os_unix.h"
 #include "joypad_iphone.h"
 
-#include "game_center.h"
-#include "icloud.h"
-#include "in_app_store.h"
 #include "ios.h"
 #include "main/input_default.h"
 #include "servers/audio_server.h"
 #include "servers/visual/rasterizer.h"
 #include "servers/visual_server.h"
 
+extern void godot_ios_plugins_initialize();
+extern void godot_ios_plugins_deinitialize();
+
 class OSIPhone : public OS_Unix {
 
 private:
@@ -57,15 +57,6 @@ private:
 
 	AudioDriverCoreAudio audio_driver;
 
-#ifdef GAME_CENTER_ENABLED
-	GameCenter *game_center;
-#endif
-#ifdef STOREKIT_ENABLED
-	InAppStore *store_kit;
-#endif
-#ifdef ICLOUD_ENABLED
-	ICloud *icloud;
-#endif
 	iOS *ios;
 
 	JoypadIPhone *joypad_iphone;

+ 2 - 32
platform/iphone/os_iphone.mm

@@ -181,21 +181,6 @@ Error OSIPhone::initialize(const VideoMode &p_desired, int p_video_driver, int p
 
 	input = memnew(InputDefault);
 
-#ifdef GAME_CENTER_ENABLED
-	game_center = memnew(GameCenter);
-	Engine::get_singleton()->add_singleton(Engine::Singleton("GameCenter", game_center));
-#endif
-
-#ifdef STOREKIT_ENABLED
-	store_kit = memnew(InAppStore);
-	Engine::get_singleton()->add_singleton(Engine::Singleton("InAppStore", store_kit));
-#endif
-
-#ifdef ICLOUD_ENABLED
-	icloud = memnew(ICloud);
-	Engine::get_singleton()->add_singleton(Engine::Singleton("ICloud", icloud));
-	//icloud->connect();
-#endif
 	ios = memnew(iOS);
 	Engine::get_singleton()->add_singleton(Engine::Singleton("iOS", ios));
 
@@ -210,6 +195,7 @@ MainLoop *OSIPhone::get_main_loop() const {
 };
 
 void OSIPhone::set_main_loop(MainLoop *p_main_loop) {
+	godot_ios_plugins_initialize();
 
 	main_loop = p_main_loop;
 
@@ -334,23 +320,7 @@ void OSIPhone::finalize() {
 		memdelete(ios);
 	}
 
-#ifdef GAME_CENTER_ENABLED
-	if (game_center) {
-		memdelete(game_center);
-	}
-#endif
-
-#ifdef STOREKIT_ENABLED
-	if (store_kit) {
-		memdelete(store_kit);
-	}
-#endif
-
-#ifdef ICLOUD_ENABLED
-	if (icloud) {
-		memdelete(icloud);
-	}
-#endif
+	godot_ios_plugins_deinitialize();
 
 	visual_server->finish();
 	memdelete(visual_server);

+ 283 - 0
platform/iphone/plugin/godot_plugin_config.h

@@ -0,0 +1,283 @@
+/*************************************************************************/
+/*  godot_plugin_config.h                                                */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#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"
+
+/*
+ The `config` section and fields are required and defined as follow:
+- **name**: name of the plugin
+- **binary**: path to static `.a` library
+
+The `dependencies` and fields are optional.
+- **linked**: dependencies that should only be linked.
+- **embedded**: dependencies that should be linked and embedded into application.
+- **system**: system dependencies that should be linked.
+- **capabilities**: capabilities that would be used for `UIRequiredDeviceCapabilities` options in Info.plist file.
+- **files**: files that would be copied into application
+
+The `plist` section are optional.
+- **key**: key and value that would be added in Info.plist file.
+ */
+
+struct PluginConfigIOS {
+
+	static const char *PLUGIN_CONFIG_EXT;
+
+	static const char *CONFIG_SECTION;
+	static const char *CONFIG_NAME_KEY;
+	static const char *CONFIG_BINARY_KEY;
+	static const char *CONFIG_INITIALIZE_KEY;
+	static const char *CONFIG_DEINITIALIZE_KEY;
+
+	static const char *DEPENDENCIES_SECTION;
+	static const char *DEPENDENCIES_LINKED_KEY;
+	static const char *DEPENDENCIES_EMBEDDED_KEY;
+	static const char *DEPENDENCIES_SYSTEM_KEY;
+	static const char *DEPENDENCIES_CAPABILITIES_KEY;
+	static const char *DEPENDENCIES_FILES_KEY;
+
+	static const char *PLIST_SECTION;
+
+	// Set to true when the config file is properly loaded.
+	bool valid_config = false;
+	bool supports_targets = false;
+	// Unix timestamp of last change to this plugin.
+	uint64_t last_updated = 0;
+
+	// Required config section
+	String name;
+	String binary;
+	String initialization_method;
+	String deinitialization_method;
+
+	// Optional dependencies section
+	Vector<String> linked_dependencies;
+	Vector<String> embedded_dependencies;
+	Vector<String> system_dependencies;
+
+	Vector<String> files_to_copy;
+	Vector<String> capabilities;
+
+	// Optional plist section
+	// Supports only string types for now
+	HashMap<String, String> plist;
+};
+
+const char *PluginConfigIOS::PLUGIN_CONFIG_EXT = ".gdip";
+
+const char *PluginConfigIOS::CONFIG_SECTION = "config";
+const char *PluginConfigIOS::CONFIG_NAME_KEY = "name";
+const char *PluginConfigIOS::CONFIG_BINARY_KEY = "binary";
+const char *PluginConfigIOS::CONFIG_INITIALIZE_KEY = "initialization";
+const char *PluginConfigIOS::CONFIG_DEINITIALIZE_KEY = "deinitialization";
+
+const char *PluginConfigIOS::DEPENDENCIES_SECTION = "dependencies";
+const char *PluginConfigIOS::DEPENDENCIES_LINKED_KEY = "linked";
+const char *PluginConfigIOS::DEPENDENCIES_EMBEDDED_KEY = "embedded";
+const char *PluginConfigIOS::DEPENDENCIES_SYSTEM_KEY = "system";
+const char *PluginConfigIOS::DEPENDENCIES_CAPABILITIES_KEY = "capabilities";
+const char *PluginConfigIOS::DEPENDENCIES_FILES_KEY = "files";
+
+const char *PluginConfigIOS::PLIST_SECTION = "plist";
+
+static inline String resolve_local_dependency_path(String plugin_config_dir, String dependency_path) {
+	String absolute_path;
+
+	if (dependency_path.empty()) {
+		return absolute_path;
+	}
+
+	if (dependency_path.is_abs_path()) {
+		return dependency_path;
+	}
+
+	String res_path = ProjectSettings::get_singleton()->globalize_path("res://");
+	absolute_path = plugin_config_dir.plus_file(dependency_path);
+
+	return absolute_path.replace(res_path, "res://");
+}
+
+static inline String resolve_system_dependency_path(String dependency_path) {
+	String absolute_path;
+
+	if (dependency_path.empty()) {
+		return absolute_path;
+	}
+
+	if (dependency_path.is_abs_path()) {
+		return dependency_path;
+	}
+
+	String system_path = "/System/Library/Frameworks";
+
+	return system_path.plus_file(dependency_path);
+}
+
+static inline Vector<String> resolve_local_dependencies(String plugin_config_dir, Vector<String> p_paths) {
+	Vector<String> paths;
+
+	for (int i = 0; i < p_paths.size(); i++) {
+		String path = resolve_local_dependency_path(plugin_config_dir, p_paths[i]);
+
+		if (path.empty()) {
+			continue;
+		}
+
+		paths.push_back(path);
+	}
+
+	return paths;
+}
+
+static inline Vector<String> resolve_system_dependencies(Vector<String> p_paths) {
+	Vector<String> paths;
+
+	for (int i = 0; i < p_paths.size(); i++) {
+		String path = resolve_system_dependency_path(p_paths[i]);
+
+		if (path.empty()) {
+			continue;
+		}
+
+		paths.push_back(path);
+	}
+
+	return paths;
+}
+
+static inline bool validate_plugin(PluginConfigIOS &plugin_config) {
+	bool valid_name = !plugin_config.name.empty();
+	bool valid_binary_name = !plugin_config.binary.empty();
+	bool valid_initialize = !plugin_config.initialization_method.empty();
+	bool valid_deinitialize = !plugin_config.deinitialization_method.empty();
+
+	bool fields_value = valid_name && valid_binary_name && valid_initialize && valid_deinitialize;
+
+	if (fields_value && FileAccess::exists(plugin_config.binary)) {
+		plugin_config.valid_config = true;
+		plugin_config.supports_targets = false;
+	} else if (fields_value) {
+		String file_path = plugin_config.binary.get_base_dir();
+		String file_name = plugin_config.binary.get_basename().get_file();
+		String release_file_name = file_path.plus_file(file_name + ".release.a");
+		String debug_file_name = file_path.plus_file(file_name + ".debug.a");
+
+		if (FileAccess::exists(release_file_name) && FileAccess::exists(debug_file_name)) {
+			plugin_config.valid_config = true;
+			plugin_config.supports_targets = true;
+		}
+	}
+
+	return plugin_config.valid_config;
+}
+
+static inline uint64_t get_plugin_modification_time(const PluginConfigIOS &plugin_config, const String &config_path) {
+	uint64_t last_updated = FileAccess::get_modified_time(config_path);
+
+	if (!plugin_config.supports_targets) {
+		last_updated = MAX(last_updated, FileAccess::get_modified_time(plugin_config.binary));
+	} else {
+		String file_path = plugin_config.binary.get_base_dir();
+		String file_name = plugin_config.binary.get_basename().get_file();
+		String release_file_name = file_path.plus_file(file_name + ".release.a");
+		String debug_file_name = file_path.plus_file(file_name + ".debug.a");
+
+		last_updated = MAX(last_updated, FileAccess::get_modified_time(release_file_name));
+		last_updated = MAX(last_updated, FileAccess::get_modified_time(debug_file_name));
+	}
+
+	return last_updated;
+}
+
+static inline PluginConfigIOS load_plugin_config(Ref<ConfigFile> config_file, const String &path) {
+	PluginConfigIOS plugin_config = {};
+
+	if (!config_file.is_valid()) {
+		return plugin_config;
+	}
+
+	Error err = config_file->load(path);
+
+	if (err != OK) {
+		return plugin_config;
+	}
+
+	String config_base_dir = path.get_base_dir();
+
+	plugin_config.name = config_file->get_value(PluginConfigIOS::CONFIG_SECTION, PluginConfigIOS::CONFIG_NAME_KEY, String());
+	plugin_config.initialization_method = config_file->get_value(PluginConfigIOS::CONFIG_SECTION, PluginConfigIOS::CONFIG_INITIALIZE_KEY, String());
+	plugin_config.deinitialization_method = config_file->get_value(PluginConfigIOS::CONFIG_SECTION, PluginConfigIOS::CONFIG_DEINITIALIZE_KEY, String());
+
+	String binary_path = config_file->get_value(PluginConfigIOS::CONFIG_SECTION, PluginConfigIOS::CONFIG_BINARY_KEY, String());
+	plugin_config.binary = resolve_local_dependency_path(config_base_dir, binary_path);
+
+	if (config_file->has_section(PluginConfigIOS::DEPENDENCIES_SECTION)) {
+		Vector<String> linked_dependencies = config_file->get_value(PluginConfigIOS::DEPENDENCIES_SECTION, PluginConfigIOS::DEPENDENCIES_LINKED_KEY, Vector<String>());
+		Vector<String> embedded_dependencies = config_file->get_value(PluginConfigIOS::DEPENDENCIES_SECTION, PluginConfigIOS::DEPENDENCIES_EMBEDDED_KEY, Vector<String>());
+		Vector<String> system_dependencies = config_file->get_value(PluginConfigIOS::DEPENDENCIES_SECTION, PluginConfigIOS::DEPENDENCIES_SYSTEM_KEY, Vector<String>());
+		Vector<String> files = config_file->get_value(PluginConfigIOS::DEPENDENCIES_SECTION, PluginConfigIOS::DEPENDENCIES_FILES_KEY, Vector<String>());
+
+		plugin_config.linked_dependencies = resolve_local_dependencies(config_base_dir, linked_dependencies);
+		plugin_config.embedded_dependencies = resolve_local_dependencies(config_base_dir, embedded_dependencies);
+		plugin_config.system_dependencies = resolve_system_dependencies(system_dependencies);
+
+		plugin_config.files_to_copy = resolve_local_dependencies(config_base_dir, files);
+
+		plugin_config.capabilities = config_file->get_value(PluginConfigIOS::DEPENDENCIES_SECTION, PluginConfigIOS::DEPENDENCIES_CAPABILITIES_KEY, Vector<String>());
+	}
+
+	if (config_file->has_section(PluginConfigIOS::PLIST_SECTION)) {
+		List<String> keys;
+		config_file->get_section_keys(PluginConfigIOS::PLIST_SECTION, &keys);
+
+		for (int i = 0; i < keys.size(); i++) {
+			String value = config_file->get_value(PluginConfigIOS::PLIST_SECTION, keys[i], String());
+
+			if (value.empty()) {
+				continue;
+			}
+
+			plugin_config.plist[keys[i]] = value;
+		}
+	}
+
+	if (validate_plugin(plugin_config)) {
+		plugin_config.last_updated = get_plugin_modification_time(plugin_config, path);
+	}
+
+	return plugin_config;
+}
+
+#endif // GODOT_PLUGIN_CONFIG_H

+ 1 - 2
platform/iphone/view_controller.h

@@ -28,14 +28,13 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
 
-#import <GameKit/GameKit.h>
 #import <UIKit/UIKit.h>
 
 @class GodotView;
 @class GodotNativeVideoView;
 @class GodotKeyboardInputView;
 
-@interface ViewController : UIViewController <GKGameCenterControllerDelegate>
+@interface ViewController : UIViewController
 
 @property(nonatomic, readonly, strong) GodotView *godotView;
 @property(nonatomic, readonly, strong) GodotNativeVideoView *videoView;

+ 0 - 8
platform/iphone/view_controller.mm

@@ -225,12 +225,4 @@
 	}
 }
 
-#ifdef GAME_CENTER_ENABLED
-- (void)gameCenterViewControllerDidFinish:(GKGameCenterViewController *)gameCenterViewController {
-	//[gameCenterViewController dismissViewControllerAnimated:YES completion:^{GameCenter::get_singleton()->game_center_closed();}];//version for signaling when overlay is completely gone
-	GameCenter::get_singleton()->game_center_closed();
-	[gameCenterViewController dismissViewControllerAnimated:YES completion:nil];
-}
-#endif
-
 @end