gdl_123 před 3 měsíci
rodič
revize
eec52b1c81

+ 1 - 1
Assets/Content/Config/DownLoadUrlCfg.csv

@@ -4,4 +4,4 @@ int,string,string,string,string,string,int
 1,GameDebug,http://43.226.57.217:88/dny/,http://43.226.57.217:81/,dny,res/AndroidNewRes/,1
 2,Android,http://43.156.13.116:88/dny/,http://43.156.13.116:81/,dny,res/AndroidNewRes/,1
 3,IOS,http://43.156.13.116:88/dny/,http://43.156.13.116:81/,dny,res/IosRes/,1
-4,ShenHe,true,http://43.156.13.116:81/,IOS,false,0
+4,ShenHe,true,http://43.156.13.116:81/serverlist/serverstate,?platform=IOS,false,0

+ 1 - 1
Assets/Editor/Pack/Platform/PackPlatformiOSBuild.cs

@@ -112,7 +112,7 @@ namespace Pack
             // 为了过审核,需要删除OpenUrl
             //File.Copy(Application.dataPath + PackConstant.BuildSharedResourcePath + "/Plugins/Special/iOS/libiPhone-lib.a", Path.Combine(outPath, "Libraries/libiPhone-lib.a"), true);
 
-            //CopymmFile(new string[] { "LuLuConnector.h", "LuLuConnector.mm" }, "Classes");
+            CopymmFile(new string[] { "UnityAppController.mm" }, "Classes");
             ChangeXCodeProject(buildOptions);
         }
 #if UNITY_IOS

+ 771 - 0
Build/BuildDependenceResource/Plugins/Special/iOS/UnityAppController.mm

@@ -0,0 +1,771 @@
+#import "UnityAppController.h"
+#import "UnityAppController+ViewHandling.h"
+#import "UnityAppController+Rendering.h"
+#import "iPhone_Sensors.h"
+
+#import <CoreGraphics/CoreGraphics.h>
+#import <QuartzCore/QuartzCore.h>
+#import <QuartzCore/CADisplayLink.h>
+#import <Availability.h>
+#import <AVFoundation/AVFoundation.h>
+
+#import <SPChannelSDK/SPChannelSDK.h>
+
+#include <mach/mach_time.h>
+
+// MSAA_DEFAULT_SAMPLE_COUNT was removed
+// ENABLE_INTERNAL_PROFILER and related defines were moved to iPhone_Profiler.h
+// kFPS define for removed: you can use Application.targetFrameRate (30 fps by default)
+// DisplayLink is the only run loop mode now - all others were removed
+
+#include "CrashReporter.h"
+
+#include "UI/OrientationSupport.h"
+#include "UI/UnityView.h"
+#include "UI/Keyboard.h"
+#include "UI/UnityViewControllerBase.h"
+#include "Unity/InternalProfiler.h"
+#include "Unity/DisplayManager.h"
+#include "Unity/ObjCRuntime.h"
+#include "PluginBase/AppDelegateListener.h"
+
+
+
+#include <assert.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/sysctl.h>
+
+// we assume that app delegate is never changed and we can cache it, instead of re-query UIApplication every time
+UnityAppController* _UnityAppController = nil;
+UnityAppController* GetAppController()
+{
+    return _UnityAppController;
+}
+
+// we keep old bools around to support "old" code that might have used them
+bool _ios81orNewer = false, _ios82orNewer = false, _ios83orNewer = false, _ios90orNewer = false, _ios91orNewer = false;
+bool _ios100orNewer = false, _ios101orNewer = false, _ios102orNewer = false, _ios103orNewer = false;
+bool _ios110orNewer = false, _ios111orNewer = false, _ios112orNewer = false;
+bool _ios130orNewer = false, _ios140orNewer = false, _ios150orNewer = false, _ios160orNewer = false;
+
+// minimal Unity initialization done, enough to do calls to provide data like URL launch
+bool _unityEngineLoaded = false;
+// was core of Unity loaded (non-graphics part prior to loading first scene)
+bool _unityEngineInitialized = false;
+// was unity rendering already inited: we should not touch rendering while this is false
+bool    _renderingInited        = false;
+// was unity inited: we should not touch unity api while this is false
+bool    _unityAppReady          = false;
+// see if there's a need to do internal player pause/resume handling
+//
+// Typically the trampoline code should manage this internally, but
+// there are use cases, videoplayer, plugin code, etc where the player
+// is paused before the internal handling comes relevant. Avoid
+// overriding externally managed player pause/resume handling by
+// caching the state
+bool    _wasPausedExternal      = false;
+// should we skip present on next draw: used in corner cases (like rotation) to fill both draw-buffers with some content
+bool    _skipPresent            = false;
+// was app "resigned active": some operations do not make sense while app is in background
+bool    _didResignActive        = false;
+
+#if UNITY_SUPPORT_ROTATION
+// Required to enable specific orientation for some presentation controllers: see supportedInterfaceOrientationsForWindow below for details
+NSInteger _forceInterfaceOrientationMask = 0;
+#endif
+
+@implementation UnityAppController
+
+@synthesize unityView               = _unityView;
+@synthesize unityDisplayLink        = _displayLink;
+
+@synthesize rootView                = _rootView;
+@synthesize rootViewController      = _rootController;
+@synthesize mainDisplay             = _mainDisplay;
+@synthesize renderDelegate          = _renderDelegate;
+@synthesize quitHandler             = _quitHandler;
+
+#if UNITY_SUPPORT_ROTATION
+@synthesize interfaceOrientation    = _curOrientation;
+#endif
+
+- (id)init
+{
+    if ((self = _UnityAppController = [super init]))
+    {
+        // due to clang issues with generating warning for overriding deprecated methods
+        // we will simply assert if deprecated methods are present
+        // NB: methods table is initied at load (before this call), so it is ok to check for override
+        NSAssert(![self respondsToSelector: @selector(createUnityViewImpl)],
+            @"createUnityViewImpl is deprecated and will not be called. Override createUnityView"
+        );
+        NSAssert(![self respondsToSelector: @selector(createViewHierarchyImpl)],
+            @"createViewHierarchyImpl is deprecated and will not be called. Override willStartWithViewController"
+        );
+        NSAssert(![self respondsToSelector: @selector(createViewHierarchy)],
+            @"createViewHierarchy is deprecated and will not be implemented. Use createUI"
+        );
+    }
+    return self;
+}
+
+- (void)setWindow:(id)object        {}
+- (UIWindow*)window                 { return _window; }
+
+
+- (void)shouldAttachRenderDelegate  {}
+- (void)preStartUnity               {}
+
+
+- (void)startUnity:(UIApplication*)application
+{
+    NSAssert(_unityAppReady == NO, @"[UnityAppController startUnity:] called after Unity has been initialized");
+
+    UnityInitApplicationGraphics();
+
+    // we make sure that first level gets correct display list and orientation
+    [[DisplayManager Instance] updateDisplayListCacheInUnity];
+
+    UnityLoadApplication();
+    Profiler_InitProfiler();
+
+    [self showGameUI];
+    [self createDisplayLink];
+
+    UnitySetPlayerFocus(1);
+
+    AVAudioSession* audioSession = [AVAudioSession sharedInstance];
+    // If Unity audio is disabled, we set the category to ambient to make sure we don't mute other app's audio. We set the audio session
+    // to active so we can get outputVolume callbacks. If Unity audio is enabled, FMOD should have already handled all of this AVAudioSession init.
+    if (!UnityIsAudioManagerAvailableAndEnabled())
+    {
+        [audioSession setCategory: AVAudioSessionCategoryAmbient error: nil];
+        [audioSession setActive: YES error: nil];
+    }
+
+    [audioSession addObserver: self forKeyPath: @"outputVolume" options: 0 context: nil];
+    UnityUpdateMuteState([audioSession outputVolume] < 0.01f ? 1 : 0);
+
+#if UNITY_REPLAY_KIT_AVAILABLE
+    void InitUnityReplayKit();  // Classes/Unity/UnityReplayKit.mm
+
+    InitUnityReplayKit();
+#endif
+}
+
+extern "C" void UnityDestroyDisplayLink()
+{
+    [GetAppController() destroyDisplayLink];
+}
+
+extern "C" void UnityRequestQuit()
+{
+    _didResignActive = true;
+    if (GetAppController().quitHandler)
+        GetAppController().quitHandler();
+    else
+        exit(0);
+}
+
+extern void SensorsCleanup();
+extern "C" void UnityCleanupTrampoline()
+{
+    // Unity view and viewController will not necessary be destroyed right after this function execution.
+    // We need to ensure that these objects will not receive any callbacks from system during that time.
+    [_UnityAppController window].rootViewController = nil;
+    [[_UnityAppController unityView] removeFromSuperview];
+
+    // Prevent multiple cleanups
+    if (_UnityAppController == nil)
+        return;
+
+    [KeyboardDelegate Destroy];
+
+    SensorsCleanup();
+
+    Profiler_UninitProfiler();
+
+    [DisplayManager Destroy];
+
+    UnityDestroyDisplayLink();
+
+    _UnityAppController = nil;
+}
+
+#if UNITY_SUPPORT_ROTATION
+
+- (NSUInteger)application:(UIApplication*)application supportedInterfaceOrientationsForWindow:(UIWindow*)window
+{
+    // No rootViewController is set because we are switching from one view controller to another, all orientations should be enabled
+    if ([window rootViewController] == nil)
+        return UIInterfaceOrientationMaskAll;
+
+    // During splash screen show phase no forced orientations should be allowed.
+    // This will prevent unwanted rotation while splash screen is on and application is not yet ready to present (Ex. Fogbugz cases: 1190428, 1269547).
+    if (!_unityAppReady)
+        return [_rootController supportedInterfaceOrientations];
+
+    // Some presentation controllers (e.g. UIImagePickerController) require portrait orientation and will throw exception if it is not supported.
+    // At the same time enabling all orientations by returning UIInterfaceOrientationMaskAll might cause unwanted orientation change
+    // (e.g. when using UIActivityViewController to "share to" another application, iOS will use supportedInterfaceOrientations to possibly reorient).
+    // So to avoid exception we are returning combination of constraints for root view controller and orientation requested by iOS.
+    // _forceInterfaceOrientationMask is updated in willChangeStatusBarOrientation, which is called if some presentation controller insists on orientation change.
+    return [[window rootViewController] supportedInterfaceOrientations] | _forceInterfaceOrientationMask;
+}
+
+- (void)application:(UIApplication*)application willChangeStatusBarOrientation:(UIInterfaceOrientation)newStatusBarOrientation duration:(NSTimeInterval)duration
+{
+    // Setting orientation mask which is requested by iOS: see supportedInterfaceOrientationsForWindow above for details
+    _forceInterfaceOrientationMask = 1 << newStatusBarOrientation;
+}
+
+#endif
+
+#if !PLATFORM_TVOS
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-implementations"
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+- (void)application:(UIApplication*)application didReceiveLocalNotification:(UILocalNotification*)notification
+{
+    AppController_SendNotificationWithArg(kUnityDidReceiveLocalNotification, notification);
+    UnitySendLocalNotification(notification);
+}
+
+#pragma clang diagnostic pop
+
+#endif
+
+#if UNITY_USES_REMOTE_NOTIFICATIONS
+- (void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo
+{
+    AppController_SendNotificationWithArg(kUnityDidReceiveRemoteNotification, userInfo);
+    UnitySendRemoteNotification(userInfo);
+}
+
+- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
+{
+    AppController_SendNotificationWithArg(kUnityDidRegisterForRemoteNotificationsWithDeviceToken, deviceToken);
+    UnitySendDeviceToken(deviceToken);
+}
+
+#if !PLATFORM_TVOS
+- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler
+{
+    AppController_SendNotificationWithArg(kUnityDidReceiveRemoteNotification, userInfo);
+    UnitySendRemoteNotification(userInfo);
+    if (handler)
+    {
+        handler(UIBackgroundFetchResultNoData);
+    }
+}
+
+#endif
+
+- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error
+{
+    AppController_SendNotificationWithArg(kUnityDidFailToRegisterForRemoteNotificationsWithError, error);
+    UnitySendRemoteNotificationError(error);
+    // alas people do not check remote notification error through api (which is clunky, i agree) so log here to have at least some visibility
+    ::printf("\nFailed to register for remote notifications:\n%s\n\n", [[error localizedDescription] UTF8String]);
+}
+
+#endif
+
+// UIApplicationOpenURLOptionsKey was added only in ios10 sdk, while we still support ios9 sdk
+- (BOOL)application:(UIApplication*)app openURL:(NSURL*)url options:(NSDictionary<NSString*, id>*)options
+{
+    id sourceApplication = options[UIApplicationOpenURLOptionsSourceApplicationKey], annotation = options[UIApplicationOpenURLOptionsAnnotationKey];
+
+    NSMutableDictionary<NSString*, id>* notifData = [NSMutableDictionary dictionaryWithCapacity: 3];
+    if (url)
+    {
+        notifData[@"url"] = url;
+        UnitySetAbsoluteURL(url.absoluteString.UTF8String);
+    }
+    if (sourceApplication) notifData[@"sourceApplication"] = sourceApplication;
+    if (annotation) notifData[@"annotation"] = annotation;
+
+    AppController_SendNotificationWithArg(kUnityOnOpenURL, notifData);
+    return [SPSDKInstance application:app openURL:url options:options];
+}
+
+- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity
+#if defined(__IPHONE_12_0) || defined(__TVOS_12_0)
+    restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring> > * _Nullable restorableObjects))restorationHandler
+#else
+    restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler
+#endif
+{
+    NSURL* url = userActivity.webpageURL;
+    if (url)
+        UnitySetAbsoluteURL(url.absoluteString.UTF8String);
+    return [SPSDKInstance application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
+}
+
+- (BOOL)application:(UIApplication*)application willFinishLaunchingWithOptions:(NSDictionary*)launchOptions
+{
+    AppController_SendNotificationWithArg(kUnityWillFinishLaunchingWithOptions, launchOptions);
+    NSURL* url = launchOptions[UIApplicationLaunchOptionsURLKey];
+    if (url != nil)
+    {
+        [self initUnityApplicationNoGraphics];
+        UnitySetAbsoluteURL(url.absoluteString.UTF8String);
+    }
+    return YES;
+}
+
+#if (PLATFORM_IOS && defined(__IPHONE_13_0)) || (PLATFORM_TVOS && defined(__TVOS_13_0))
+- (UIWindowScene*)pickStartupWindowScene:(NSSet<UIScene*>*)scenes API_AVAILABLE(ios(13.0), tvos(13.0))
+{
+    // if we have scene with UISceneActivationStateForegroundActive - pick it
+    // otherwise UISceneActivationStateForegroundInactive will work
+    //   it will be the scene going into active state
+    // if there were no active/inactive scenes (only background) we should allow background scene
+    //   this might happen in some cases with native plugins doing "things"
+    UIWindowScene *foregroundScene = nil, *backgroundScene = nil;
+    for (UIScene* scene in scenes)
+    {
+        if (![scene isKindOfClass: [UIWindowScene class]])
+            continue;
+        UIWindowScene* windowScene = (UIWindowScene*)scene;
+
+        if (scene.activationState == UISceneActivationStateForegroundActive)
+            return windowScene;
+        if (scene.activationState == UISceneActivationStateForegroundInactive)
+            foregroundScene = windowScene;
+        else if (scene.activationState == UISceneActivationStateBackground)
+            backgroundScene = windowScene;
+    }
+
+    return foregroundScene ? foregroundScene : backgroundScene;
+}
+#endif
+
+- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
+{
+    ::printf("-> applicationDidFinishLaunching()\n");
+    
+    [SPSDKInstance application:application didFinishLaunchingWithOptions:launchOptions];
+    // send notfications
+#if !PLATFORM_TVOS
+
+    #pragma clang diagnostic push
+    #pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+    if (UILocalNotification* notification = [launchOptions objectForKey: UIApplicationLaunchOptionsLocalNotificationKey])
+        UnitySendLocalNotification(notification);
+
+    if ([UIDevice currentDevice].generatesDeviceOrientationNotifications == NO)
+        [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
+
+    #pragma clang diagnostic pop
+
+#endif
+
+    if ([self isBackgroundLaunchOptions: launchOptions])
+        return YES;
+
+    [self initUnityWithApplication: application];
+    return YES;
+}
+
+- (BOOL)isBackgroundLaunchOptions:(NSDictionary*)launchOptions
+{
+    if (launchOptions.count == 0)
+        return NO;
+
+    // launch due to location event, the app likely will stay in background
+    BOOL locationLaunch = [[launchOptions valueForKey: UIApplicationLaunchOptionsLocationKey] boolValue];
+    if (locationLaunch)
+        return YES;
+    return NO;
+}
+
+- (void)initUnityApplicationNoGraphics
+{
+    if (_unityEngineLoaded)
+        return;
+    _unityEngineLoaded = true;
+    UnityInitApplicationNoGraphics(UnityDataBundleDir());
+}
+
+- (void)initUnityWithApplication:(UIApplication*)application
+{
+    if (_unityEngineInitialized)
+        return;
+    _unityEngineInitialized = true;
+
+    // basic unity init
+    [self initUnityApplicationNoGraphics];
+
+    [self selectRenderingAPI];
+    [UnityRenderingView InitializeForAPI: self.renderingAPI];
+
+#if (PLATFORM_IOS && defined(__IPHONE_13_0)) || (PLATFORM_TVOS && defined(__TVOS_13_0))
+    if (@available(iOS 13, tvOS 13, *))
+        _window = [[UIWindow alloc] initWithWindowScene: [self pickStartupWindowScene: application.connectedScenes]];
+    else
+#endif
+    _window = [[UIWindow alloc] initWithFrame: [UIScreen mainScreen].bounds];
+
+    _unityView = [self createUnityView];
+
+
+    [DisplayManager Initialize];
+    _mainDisplay = [DisplayManager Instance].mainDisplay;
+    [_mainDisplay createWithWindow: _window andView: _unityView];
+
+    [self createUI];
+    [self preStartUnity];
+
+    // if you wont use keyboard you may comment it out at save some memory
+    [KeyboardDelegate Initialize];
+
+#if UNITY_DEVELOPER_BUILD
+    // Causes a black screen after splash screen, but would deadlock if waiting for manged debugger otherwise
+    [self performSelector: @selector(startUnity:) withObject: application afterDelay: 0];
+#else
+    [self startUnity: application];
+#endif
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context
+{
+    if ([keyPath isEqual: @"outputVolume"])
+    {
+        UnityUpdateMuteState([[AVAudioSession sharedInstance] outputVolume] < 0.01f ? 1 : 0);
+    }
+}
+
+- (void)applicationDidEnterBackground:(UIApplication*)application
+{
+    ::printf("-> applicationDidEnterBackground()\n");
+    [SPSDKInstance applicationDidEnterBackground:application];
+}
+
+- (void)applicationWillEnterForeground:(UIApplication*)application
+{
+    ::printf("-> applicationWillEnterForeground()\n");
+
+    [SPSDKInstance applicationWillEnterForeground:application];
+    
+    // applicationWillEnterForeground: might sometimes arrive *before* actually initing unity (e.g. locking on startup)
+    if (_unityAppReady)
+    {
+        // if we were showing video before going to background - the view size may be changed while we are in background
+        [GetAppController().unityView recreateRenderingSurfaceIfNeeded];
+    }
+}
+
+- (void)applicationDidBecomeActive:(UIApplication*)application
+{
+    ::printf("-> applicationDidBecomeActive()\n");
+
+    [SPSDKInstance applicationDidBecomeActive:application];
+    
+    [self removeSnapshotViewController];
+
+    if (_unityAppReady)
+    {
+        if (UnityIsPaused() && _wasPausedExternal == false)
+        {
+            UnityWillResume();
+            UnityPause(0);
+        }
+        if (_wasPausedExternal)
+        {
+            if (UnityIsFullScreenPlaying())
+                TryResumeFullScreenVideo();
+        }
+        // need to do this with delay because FMOD restarts audio in AVAudioSessionInterruptionNotification handler
+        [self performSelector: @selector(updateUnityAudioOutput) withObject: nil afterDelay: 0.1];
+        UnitySetPlayerFocus(1);
+    }
+    else
+    {
+        [self initUnityWithApplication: application];
+    }
+
+    _didResignActive = false;
+}
+
+- (void)updateUnityAudioOutput
+{
+    UnityUpdateMuteState([[AVAudioSession sharedInstance] outputVolume] < 0.01f ? 1 : 0);
+}
+
+- (void)addSnapshotViewController
+{
+    if (!_didResignActive || self->_snapshotViewController)
+    {
+        return;
+    }
+
+    UIView* snapshotView = [self createSnapshotView];
+
+    if (snapshotView != nil)
+    {
+        UIViewController* snapshotViewController = [AllocUnityViewController() init];
+        snapshotViewController.modalPresentationStyle = UIModalPresentationFullScreen;
+        snapshotViewController.view = snapshotView;
+
+        [self->_rootController presentViewController: snapshotViewController animated: false completion: nil];
+        self->_snapshotViewController = snapshotViewController;
+    }
+}
+
+- (void)removeSnapshotViewController
+{
+    // do this on the main queue async so that if we try to create one
+    // and remove in the same frame, this always happens after in the same queue
+    dispatch_async(dispatch_get_main_queue(), ^{
+        if (self->_snapshotViewController)
+        {
+            // we've got a view on top of the snapshot view (3rd party plugin/social media login etc).
+            if (self->_snapshotViewController.presentedViewController)
+            {
+                [self performSelector: @selector(removeSnapshotViewController) withObject: nil afterDelay: 0.05];
+                return;
+            }
+
+            [self->_snapshotViewController dismissViewControllerAnimated: NO completion: nil];
+            self->_snapshotViewController = nil;
+
+            // Make sure that the keyboard input field regains focus after the application becomes active.
+            [[KeyboardDelegate Instance] becomeFirstResponder];
+        }
+    });
+}
+
+- (void)applicationWillResignActive:(UIApplication*)application
+{
+    ::printf("-> applicationWillResignActive()\n");
+    
+    [SPSDKInstance applicationWillResignActive:application];
+    
+    if (_unityAppReady)
+    {
+        UnitySetPlayerFocus(0);
+
+        // signal unity that the frame rendering have ended
+        // as we will not get the callback from the display link current frame
+        UnityDisplayLinkCallback(0);
+
+        _wasPausedExternal = UnityIsPaused();
+        if (_wasPausedExternal == false)
+        {
+            // Pause Unity only if we don't need special background processing
+            // otherwise batched player loop can be called to run user scripts.
+            if (!UnityGetUseCustomAppBackgroundBehavior())
+            {
+#if UNITY_SNAPSHOT_VIEW_ON_APPLICATION_PAUSE
+                // Force player to do one more frame, so scripts get a chance to render custom screen for minimized app in task manager.
+                // NB: UnityWillPause will schedule OnApplicationPause message, which will be sent normally inside repaint (unity player loop)
+                // NB: We will actually pause after the loop (when calling UnityPause).
+                UnityWillPause();
+                [self repaint];
+                UnityWaitForFrame();
+
+                [self addSnapshotViewController];
+#endif
+                UnityPause(1);
+            }
+        }
+    }
+
+    _didResignActive = true;
+}
+
+- (void)applicationDidReceiveMemoryWarning:(UIApplication*)application
+{
+    // We handle it by subscribing for notification, this one is only a placeholder for stuff like forwarding in UaaL setups
+}
+
+- (void)applicationWillTerminate:(UIApplication*)application
+{
+    ::printf("-> applicationWillTerminate()\n");
+    
+    [SPSDKInstance applicationWillTerminate:application];
+    
+    // Only clean up if Unity has finished initializing, else the clean up process will crash,
+    // this happens if the app is force closed immediately after opening it.
+    if (_unityAppReady)
+    {
+        UnityCleanup();
+        UnityCleanupTrampoline();
+    }
+}
+
+- (void)application:(UIApplication*)application handleEventsForBackgroundURLSession:(nonnull NSString *)identifier completionHandler:(nonnull void (^)())completionHandler
+{
+    NSDictionary* arg = @{identifier: completionHandler};
+    AppController_SendNotificationWithArg(kUnityHandleEventsForBackgroundURLSession, arg);
+}
+
+@end
+
+
+void AppController_SendNotification(NSString* name)
+{
+    [[NSNotificationCenter defaultCenter] postNotificationName: name object: GetAppController()];
+}
+
+void AppController_SendNotificationWithArg(NSString* name, id arg)
+{
+    [[NSNotificationCenter defaultCenter] postNotificationName: name object: GetAppController() userInfo: arg];
+}
+
+void AppController_SendUnityViewControllerNotification(NSString* name)
+{
+    [[NSNotificationCenter defaultCenter] postNotificationName: name object: UnityGetGLViewController()];
+}
+
+extern "C" UIWindow*            UnityGetMainWindow()
+{
+    return GetAppController().mainDisplay.window;
+}
+
+extern "C" UIViewController*    UnityGetGLViewController()
+{
+    return GetAppController().rootViewController;
+}
+
+extern "C" UIView*              UnityGetGLView()
+{
+    return GetAppController().unityView;
+}
+
+extern "C" ScreenOrientation    UnityCurrentOrientation()   { return GetAppController().unityView.contentOrientation; }
+
+
+bool LogToNSLogHandler(LogType logType, const char* log, va_list list)
+{
+    NSLogv([NSString stringWithUTF8String: log], list);
+    return true;
+}
+
+static void AddNewAPIImplIfNeeded();
+
+// From https://stackoverflow.com/questions/4744826/detecting-if-ios-app-is-run-in-debugger
+static bool isDebuggerAttachedToConsole(void)
+// Returns true if the current process is being debugged (either
+// running under the debugger or has a debugger attached post facto).
+{
+    int                 junk;
+    int                 mib[4];
+    struct kinfo_proc   info;
+    size_t              size;
+
+    // Initialize the flags so that, if sysctl fails for some bizarre
+    // reason, we get a predictable result.
+
+    info.kp_proc.p_flag = 0;
+
+    // Initialize mib, which tells sysctl the info we want, in this case
+    // we're looking for information about a specific process ID.
+
+    mib[0] = CTL_KERN;
+    mib[1] = KERN_PROC;
+    mib[2] = KERN_PROC_PID;
+    mib[3] = getpid();
+
+    // Call sysctl.
+
+    size = sizeof(info);
+    junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
+    assert(junk == 0);
+
+    // We're being debugged if the P_TRACED flag is set.
+
+    return ((info.kp_proc.p_flag & P_TRACED) != 0);
+}
+
+void UnityInitTrampoline()
+{
+    InitCrashHandling();
+
+    NSString* version = [[UIDevice currentDevice] systemVersion];
+#define CHECK_VER(s) [version compare: s options: NSNumericSearch] != NSOrderedAscending
+    _ios81orNewer  = CHECK_VER(@"8.1");  _ios82orNewer  = CHECK_VER(@"8.2");  _ios83orNewer  = CHECK_VER(@"8.3");
+    _ios90orNewer  = CHECK_VER(@"9.0");  _ios91orNewer  = CHECK_VER(@"9.1");
+    _ios100orNewer = CHECK_VER(@"10.0"); _ios101orNewer = CHECK_VER(@"10.1"); _ios102orNewer = CHECK_VER(@"10.2"); _ios103orNewer = CHECK_VER(@"10.3");
+    _ios110orNewer = CHECK_VER(@"11.0"); _ios111orNewer = CHECK_VER(@"11.1"); _ios112orNewer = CHECK_VER(@"11.2");
+    _ios130orNewer  = CHECK_VER(@"13.0"); _ios140orNewer = CHECK_VER(@"14.0"); _ios150orNewer = CHECK_VER(@"15.0");
+    _ios160orNewer = CHECK_VER(@"16.0");
+#undef CHECK_VER
+
+    AddNewAPIImplIfNeeded();
+
+#if !TARGET_IPHONE_SIMULATOR
+    // Use NSLog logging if a debugger is not attached, otherwise we write to stdout.
+    if (!isDebuggerAttachedToConsole())
+        UnitySetLogEntryHandler(LogToNSLogHandler);
+#endif
+}
+
+extern "C" bool UnityiOS81orNewer() { return _ios81orNewer; }
+extern "C" bool UnityiOS82orNewer() { return _ios82orNewer; }
+extern "C" bool UnityiOS90orNewer() { return _ios90orNewer; }
+extern "C" bool UnityiOS91orNewer() { return _ios91orNewer; }
+extern "C" bool UnityiOS100orNewer() { return _ios100orNewer; }
+extern "C" bool UnityiOS101orNewer() { return _ios101orNewer; }
+extern "C" bool UnityiOS102orNewer() { return _ios102orNewer; }
+extern "C" bool UnityiOS103orNewer() { return _ios103orNewer; }
+extern "C" bool UnityiOS110orNewer() { return _ios110orNewer; }
+extern "C" bool UnityiOS111orNewer() { return _ios111orNewer; }
+extern "C" bool UnityiOS112orNewer() { return _ios112orNewer; }
+extern "C" bool UnityiOS130orNewer() { return _ios130orNewer; }
+extern "C" bool UnityiOS140orNewer() { return _ios140orNewer; }
+extern "C" bool UnityiOS150orNewer() { return _ios150orNewer; }
+extern "C" bool UnityiOS160orNewer() { return _ios160orNewer; }
+
+// sometimes apple adds new api with obvious fallback on older ios.
+// in that case we simply add these functions ourselves to simplify code
+static void AddNewAPIImplIfNeeded()
+{
+    if (![[UIScreen class] instancesRespondToSelector: @selector(maximumFramesPerSecond)])
+    {
+        IMP UIScreen_MaximumFramesPerSecond_IMP = imp_implementationWithBlock(^NSInteger(id _self) {
+            return 60;
+        });
+        class_replaceMethod([UIScreen class], @selector(maximumFramesPerSecond), UIScreen_MaximumFramesPerSecond_IMP, UIScreen_maximumFramesPerSecond_Enc);
+    }
+
+    if (![[UIView class] instancesRespondToSelector: @selector(safeAreaInsets)])
+    {
+        IMP UIView_SafeAreaInsets_IMP = imp_implementationWithBlock(^UIEdgeInsets(id _self) {
+            return UIEdgeInsetsZero;
+        });
+        class_replaceMethod([UIView class], @selector(safeAreaInsets), UIView_SafeAreaInsets_IMP, UIView_safeAreaInsets_Enc);
+    }
+}
+
+// xcode11 uses new compiler-rt lib
+// if we build unity player lib with xcode11 and then user links final project with older xcode
+//   the link fails with Undefined Symbol ___isPlatformVersionAtLeast
+// hence we add this as a temporary hack until we start requiring xcode11
+
+#if __clang_major__ < 11
+extern "C" int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor);
+extern "C" int32_t __isPlatformVersionAtLeast(uint32_t Platform, uint32_t Major, uint32_t Minor, uint32_t Subminor)
+{
+    return __isOSVersionAtLeast(Major, Minor, Subminor);
+}
+
+#endif
+
+// starting with xcode 11.4 apple changed FD_SET and related macro to use weakly imported __darwin_check_fd_set_overflow
+// alas if we build xcode project with OLDER xcode this function is missing
+//   and we build unity lib with xcode11+, thus producing linker error
+// we mimic the logic of apple sdk itself (this part is open sourced):
+//   if __darwin_check_fd_set_overflow is not present the caller returns 1, so do we
+#ifndef __IPHONE_13_4
+extern "C" int __darwin_check_fd_set_overflow(int, const void *, int)
+{
+    return 1;
+}
+
+#endif

+ 2 - 2
ProjectSettings/ProjectSettings.asset

@@ -17,7 +17,7 @@ PlayerSettings:
   defaultCursor: {fileID: 0}
   cursorHotspot: {x: 0, y: 0}
   m_SplashScreenBackgroundColor: {r: 1, g: 1, b: 1, a: 1}
-  m_ShowUnitySplashScreen: 0
+  m_ShowUnitySplashScreen: 1
   m_ShowUnitySplashLogo: 1
   m_SplashScreenOverlayOpacity: 1
   m_SplashScreenAnimation: 0
@@ -188,7 +188,7 @@ PlayerSettings:
   tvOSRequireExtendedGameController: 0
   tvOSTargetOSVersionString: 12.0
   uIPrerenderedIcon: 0
-  uIRequiresPersistentWiFi: 0
+  uIRequiresPersistentWiFi: 1
   uIRequiresFullScreen: 1
   uIStatusBarHidden: 1
   uIExitOnSuspend: 0