summaryrefslogtreecommitdiff
path: root/platform/iphone
diff options
context:
space:
mode:
Diffstat (limited to 'platform/iphone')
-rw-r--r--platform/iphone/SCsub3
-rw-r--r--platform/iphone/app_delegate.mm64
-rw-r--r--platform/iphone/detect.py27
-rw-r--r--platform/iphone/game_center.h6
-rw-r--r--platform/iphone/game_center.mm228
-rwxr-xr-xplatform/iphone/gl_view.h2
-rwxr-xr-xplatform/iphone/gl_view.mm7
-rw-r--r--platform/iphone/icloud.h66
-rw-r--r--platform/iphone/icloud.mm379
-rw-r--r--platform/iphone/in_app_store.mm11
-rw-r--r--platform/iphone/os_iphone.cpp9
-rw-r--r--platform/iphone/os_iphone.h7
-rw-r--r--platform/iphone/view_controller.h3
-rw-r--r--platform/iphone/view_controller.mm9
14 files changed, 790 insertions, 31 deletions
diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub
index d495e3b5fc..922a324694 100644
--- a/platform/iphone/SCsub
+++ b/platform/iphone/SCsub
@@ -12,6 +12,7 @@ iphone_lib = [
'view_controller.mm',
'game_center.mm',
'in_app_store.mm',
+ 'icloud.mm',
'Appirater.m',
]
@@ -34,5 +35,5 @@ obj = env_ios.Object('godot_iphone.cpp')
prog = None
prog = env_ios.Program('#bin/godot', [obj] + iphone_lib)
-action = "dsymutil "+File(prog)[0].path+" -o " + File(prog)[0].path + ".dSYM"
+action = "$IPHONEPATH/usr/bin/dsymutil "+File(prog)[0].path+" -o " + File(prog)[0].path + ".dSYM"
env.AddPostAction(prog, action)
diff --git a/platform/iphone/app_delegate.mm b/platform/iphone/app_delegate.mm
index 2c7a0a0790..d5764b2b5c 100644
--- a/platform/iphone/app_delegate.mm
+++ b/platform/iphone/app_delegate.mm
@@ -43,6 +43,11 @@
#import <AdSupport/AdSupport.h>
#endif
+#ifdef MODULE_PARSE_ENABLED
+#import <Parse/Parse.h>
+#import "FBSDKCoreKit/FBSDKCoreKit.h"
+#endif
+
#define kFilteringFactor 0.1
#define kRenderingFrequency 60
#define kAccelerometerFrequency 100.0 // Hz
@@ -138,6 +143,30 @@ static int frame_count = 0;
Main::setup2();
++frame_count;
+ // this might be necessary before here
+ NSDictionary* dict = [[NSBundle mainBundle] infoDictionary];
+ for (NSString* key in dict) {
+ NSObject* value = [dict objectForKey:key];
+ String ukey = String::utf8([key UTF8String]);
+
+ // we need a NSObject to Variant conversor
+
+ if ([value isKindOfClass:[NSString class]]) {
+ NSString* str = (NSString*)value;
+ String uval = String::utf8([str UTF8String]);
+
+ Globals::get_singleton()->set("Info.plist/"+ukey, uval);
+
+ } else if ([value isKindOfClass:[NSNumber class]]) {
+
+ NSNumber* n = (NSNumber*)value;
+ double dval = [n doubleValue];
+
+ Globals::get_singleton()->set("Info.plist/"+ukey, dval);
+ };
+ // do stuff
+ }
+
} break;
/*
case 3: {
@@ -318,6 +347,15 @@ static int frame_count = 0;
// For 4.2+ support
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
+#ifdef MODULE_PARSE_ENABLED
+ NSLog(@"Handling application openURL");
+ return [[FBSDKApplicationDelegate sharedInstance] application:application
+ openURL:url
+ sourceApplication:sourceApplication
+ annotation:annotation];
+#endif
+
+
#ifdef MODULE_FACEBOOKSCORER_IOS_ENABLED
return [[[FacebookScorer sharedInstance] facebook] handleOpenURL:url];
#else
@@ -325,6 +363,32 @@ static int frame_count = 0;
#endif
}
+- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
+#ifdef MODULE_PARSE_ENABLED
+ // Store the deviceToken in the current installation and save it to Parse.
+ PFInstallation *currentInstallation = [PFInstallation currentInstallation];
+ //NSString* token = [[NSString alloc] initWithData:deviceToken encoding:NSUTF8StringEncoding];
+ NSLog(@"Device Token : %@ ", deviceToken);
+ [currentInstallation setDeviceTokenFromData:deviceToken];
+ [currentInstallation saveInBackground];
+#endif
+}
+
+- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
+#ifdef MODULE_PARSE_ENABLED
+ [PFPush handlePush:userInfo];
+ NSDictionary *aps = [userInfo objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+
+ NSLog(@"Push Notification Payload (app active) %@", aps);
+ [defaults setObject:aps forKey:@"notificationInfo"];
+ [defaults synchronize];
+ if (application.applicationState == UIApplicationStateInactive) {
+ [PFAnalytics trackAppOpenedWithRemoteNotificationPayload:userInfo];
+ }
+#endif
+}
+
- (void)dealloc
{
[window release];
diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py
index fb57876a83..137fcc56f5 100644
--- a/platform/iphone/detect.py
+++ b/platform/iphone/detect.py
@@ -11,7 +11,8 @@ def get_name():
def can_build():
import sys
- if sys.platform == 'darwin':
+ import os
+ if sys.platform == 'darwin' or os.environ.has_key("OSXCROSS_IOS"):
return True
return False
@@ -25,9 +26,11 @@ def get_opts():
('IPHONESDK', 'path to the iphone SDK', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/${IOS_SDK_VERSION}.sdk/'),
('game_center', 'Support for game center', 'yes'),
('store_kit', 'Support for in-app store', 'yes'),
+ ('icloud', 'Support for iCloud', 'yes'),
('ios_gles22_override', 'Force GLES2.0 on iOS', 'yes'),
('ios_appirater', 'Enable Appirater', 'no'),
('ios_exceptions', 'Use exceptions when compiling on playbook', 'yes'),
+ ('ios_triple', 'Triple for ios toolchain', ''),
]
def get_flags():
@@ -48,9 +51,10 @@ def configure(env):
# env['CC'] = '$IPHONEPATH/Developer/usr/bin/gcc'
# env['CXX'] = '$IPHONEPATH/Developer/usr/bin/g++'
- env['CC'] = '$IPHONEPATH/usr/bin/clang'
- env['CXX'] = '$IPHONEPATH/usr/bin/clang++'
- env['AR'] = 'ar'
+ env['CC'] = '$IPHONEPATH/usr/bin/${ios_triple}clang'
+ env['CXX'] = '$IPHONEPATH/usr/bin/${ios_triple}clang++'
+ env['AR'] = '$IPHONEPATH/usr/bin/${ios_triple}ar'
+ env['RANLIB'] = '$IPHONEPATH/usr/bin/${ios_triple}ranlib'
import string
if (env["bits"]=="64"):
@@ -59,7 +63,7 @@ def configure(env):
env.Append(CPPFLAGS=['-DNEED_LONG_INT'])
env.Append(CPPFLAGS=['-DLIBYUV_DISABLE_NEON'])
else:
- env['CCFLAGS'] = string.split('-fno-objc-arc -arch armv7 -fmessage-length=0 -fno-strict-aliasing -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits -Wno-trigraphs -fpascal-strings -Wmissing-prototypes -Wreturn-type -Wparentheses -Wswitch -Wno-unused-parameter -Wunused-variable -Wunused-value -Wno-shorten-64-to-32 -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk -gdwarf-2 -fvisibility=hidden -Wno-sign-conversion -mthumb "-DIBOutlet=__attribute__((iboutlet))" "-DIBOutletCollection(ClassName)=__attribute__((iboutletcollection(ClassName)))" "-DIBAction=void)__attribute__((ibaction)" -miphoneos-version-min=4.3 -MMD -MT dependencies -isysroot $IPHONESDK')
+ env['CCFLAGS'] = string.split('-fno-objc-arc -arch armv7 -fmessage-length=0 -fno-strict-aliasing -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits -Wno-trigraphs -fpascal-strings -Wmissing-prototypes -Wreturn-type -Wparentheses -Wswitch -Wno-unused-parameter -Wunused-variable -Wunused-value -Wno-shorten-64-to-32 -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk -gdwarf-2 -fvisibility=hidden -Wno-sign-conversion -mthumb "-DIBOutlet=__attribute__((iboutlet))" "-DIBOutletCollection(ClassName)=__attribute__((iboutletcollection(ClassName)))" "-DIBAction=void)__attribute__((ibaction)" -miphoneos-version-min=5.1.1 -MMD -MT dependencies -isysroot $IPHONESDK')
if (env["bits"]=="64"):
env.Append(LINKFLAGS=['-arch', 'arm64', '-Wl,-dead_strip', '-miphoneos-version-min=5.1.1',
@@ -80,7 +84,7 @@ def configure(env):
'-framework', 'CoreMedia',
])
else:
- env.Append(LINKFLAGS=['-arch', 'armv7', '-Wl,-dead_strip', '-miphoneos-version-min=4.3',
+ env.Append(LINKFLAGS=['-arch', 'armv7', '-Wl,-dead_strip', '-miphoneos-version-min=5.1.1',
'-isysroot', '$IPHONESDK',
'-framework', 'Foundation',
'-framework', 'UIKit',
@@ -104,6 +108,9 @@ def configure(env):
if env['store_kit'] == 'yes':
env.Append(CPPFLAGS=['-DSTOREKIT_ENABLED'])
env.Append(LINKFLAGS=['-framework', 'StoreKit'])
+
+ if env['icloud'] == 'yes':
+ env.Append(CPPFLAGS=['-DICLOUD_ENABLED'])
env.Append(CPPPATH = ['$IPHONESDK/usr/include', '$IPHONESDK/System/Library/Frameworks/OpenGLES.framework/Headers', '$IPHONESDK/System/Library/Frameworks/AudioUnit.framework/Headers'])
@@ -130,6 +137,14 @@ def configure(env):
env['ENV']['CODESIGN_ALLOCATE'] = '/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/codesign_allocate'
env.Append(CPPFLAGS=['-DIPHONE_ENABLED', '-DUNIX_ENABLED', '-DGLES2_ENABLED', '-DMPC_FIXED_POINT'])
+
+ if(env["opus"]=="yes"):
+ env.opus_fixed_point="yes"
+ if(env["bits"]=="64"):
+ env.Append(CFLAGS=["-DOPUS_ARM64_OPT"])
+ else:
+ env.Append(CFLAGS=["-DOPUS_ARM_OPT"])
+
if env['ios_exceptions'] == 'yes':
env.Append(CPPFLAGS=['-fexceptions'])
else:
diff --git a/platform/iphone/game_center.h b/platform/iphone/game_center.h
index 1f4820a3c2..8f180d1638 100644
--- a/platform/iphone/game_center.h
+++ b/platform/iphone/game_center.h
@@ -51,6 +51,12 @@ public:
Error post_score(Variant p_score);
Error award_achievement(Variant p_params);
+ void reset_achievements();
+ void request_achievements();
+ void request_achievement_descriptions();
+ Error show_game_center(Variant p_params);
+
+ void game_center_closed();
int get_pending_event_count();
Variant pop_pending_event();
diff --git a/platform/iphone/game_center.mm b/platform/iphone/game_center.mm
index fd1e5f3be7..79c056776d 100644
--- a/platform/iphone/game_center.mm
+++ b/platform/iphone/game_center.mm
@@ -32,6 +32,7 @@
extern "C" {
#import <GameKit/GameKit.h>
+#import "app_delegate.h"
};
GameCenter* GameCenter::instance = NULL;
@@ -42,6 +43,10 @@ void GameCenter::_bind_methods() {
ObjectTypeDB::bind_method(_MD("post_score"),&GameCenter::post_score);
ObjectTypeDB::bind_method(_MD("award_achievement"),&GameCenter::award_achievement);
+ ObjectTypeDB::bind_method(_MD("reset_achievements"),&GameCenter::reset_achievements);
+ ObjectTypeDB::bind_method(_MD("request_achievements"),&GameCenter::request_achievements);
+ ObjectTypeDB::bind_method(_MD("request_achievement_descriptions"),&GameCenter::request_achievement_descriptions);
+ ObjectTypeDB::bind_method(_MD("show_game_center"),&GameCenter::show_game_center);
ObjectTypeDB::bind_method(_MD("get_pending_event_count"),&GameCenter::get_pending_event_count);
ObjectTypeDB::bind_method(_MD("pop_pending_event"),&GameCenter::pop_pending_event);
@@ -50,23 +55,41 @@ void GameCenter::_bind_methods() {
Error GameCenter::connect() {
- GKLocalPlayer* player = [GKLocalPlayer localPlayer];
- [player authenticateWithCompletionHandler:^(NSError* error) {
+ //if this class isn't available, game center isn't implemented
+ if ((NSClassFromString(@"GKLocalPlayer")) == nil) {
+ GameCenter::get_singleton()->connected = false;
+ return ERR_UNAVAILABLE;
+ }
- Dictionary ret;
- ret["type"] = "authentication";
- if (player.isAuthenticated) {
- ret["result"] = "ok";
- GameCenter::get_singleton()->connected = true;
- } else {
- ret["result"] = "error";
- ret["error_code"] = error.code;
- ret["error_description"] = [error.localizedDescription UTF8String];
- GameCenter::get_singleton()->connected = false;
- };
+ GKLocalPlayer* player = [GKLocalPlayer localPlayer];
+ ERR_FAIL_COND_V(![player respondsToSelector:@selector(authenticateHandler)], ERR_UNAVAILABLE);
+
+ ViewController *root_controller=(ViewController *)((AppDelegate *)[[UIApplication sharedApplication] delegate]).window.rootViewController;
+ ERR_FAIL_COND_V(!root_controller, FAILED);
+
+ //this handler is called serveral times. first when the view needs to be shown, then again after the view is cancelled or the user logs in. or if the user's already logged in, it's called just once to confirm they're authenticated. This is why no result needs to be specified in the presentViewController phase. in this case, more calls to this function will follow.
+ player.authenticateHandler = (^(UIViewController *controller, NSError *error) {
+ if (controller) {
+ [root_controller presentViewController:controller animated:YES completion:nil];
+ }
+ else {
+ Dictionary ret;
+ ret["type"] = "authentication";
+ if (player.isAuthenticated) {
+ ret["result"] = "ok";
+ GameCenter::get_singleton()->connected = true;
+ } else {
+ ret["result"] = "error";
+ ret["error_code"] = error.code;
+ ret["error_description"] = [error.localizedDescription UTF8String];
+ GameCenter::get_singleton()->connected = false;
+ };
+
+ pending_events.push_back(ret);
+ };
+
+ });
- pending_events.push_back(ret);
- }];
return OK;
};
@@ -85,7 +108,9 @@ Error GameCenter::post_score(Variant p_score) {
GKScore* reporter = [[[GKScore alloc] initWithCategory:cat_str] autorelease];
reporter.value = score;
- [reporter reportScoreWithCompletionHandler:^(NSError* error) {
+ ERR_FAIL_COND_V([GKScore respondsToSelector:@selector(reportScores)], ERR_UNAVAILABLE);
+
+ [GKScore reportScores:@[reporter] withCompletionHandler:^(NSError* error) {
Dictionary ret;
ret["type"] = "post_score";
@@ -114,8 +139,15 @@ Error GameCenter::award_achievement(Variant p_params) {
GKAchievement* achievement = [[[GKAchievement alloc] initWithIdentifier: name_str] autorelease];
ERR_FAIL_COND_V(!achievement, FAILED);
+ ERR_FAIL_COND_V([GKAchievement respondsToSelector:@selector(reportAchievements)], ERR_UNAVAILABLE);
+
achievement.percentComplete = progress;
- [achievement reportAchievementWithCompletionHandler:^(NSError* error) {
+ achievement.showsCompletionBanner = NO;
+ if (params.has("show_completion_banner")) {
+ achievement.showsCompletionBanner = params["show_completion_banner"] ? YES : NO;
+ }
+
+ [GKAchievement reportAchievements:@[achievement] withCompletionHandler:^(NSError *error) {
Dictionary ret;
ret["type"] = "award_achievement";
@@ -132,6 +164,168 @@ Error GameCenter::award_achievement(Variant p_params) {
return OK;
};
+void GameCenter::request_achievement_descriptions() {
+
+ [GKAchievementDescription loadAchievementDescriptionsWithCompletionHandler:^(NSArray *descriptions, NSError *error) {
+
+ Dictionary ret;
+ ret["type"] = "achievement_descriptions";
+ if (error == nil) {
+ ret["result"] = "ok";
+ StringArray names;
+ StringArray titles;
+ StringArray unachieved_descriptions;
+ StringArray achieved_descriptions;
+ IntArray maximum_points;
+ Array hidden;
+ Array replayable;
+
+ for (int i=0; i<[descriptions count]; i++) {
+
+ GKAchievementDescription* description = [descriptions objectAtIndex:i];
+
+ const char* str = [description.identifier UTF8String];
+ names.push_back(String::utf8(str != NULL ? str : ""));
+
+ str = [description.title UTF8String];
+ titles.push_back(String::utf8(str != NULL ? str : ""));
+
+ str = [description.unachievedDescription UTF8String];
+ unachieved_descriptions.push_back(String::utf8(str != NULL ? str : ""));
+
+ str = [description.achievedDescription UTF8String];
+ achieved_descriptions.push_back(String::utf8(str != NULL ? str : ""));
+
+ maximum_points.push_back(description.maximumPoints);
+
+ hidden.push_back(description.hidden == YES);
+
+ replayable.push_back(description.replayable == YES);
+ }
+
+ ret["names"] = names;
+ ret["titles"] = titles;
+ ret["unachieved_descriptions"] = unachieved_descriptions;
+ ret["achieved_descriptions"] = achieved_descriptions;
+ ret["maximum_points"] = maximum_points;
+ ret["hidden"] = hidden;
+ ret["replayable"] = replayable;
+
+ } else {
+ ret["result"] = "error";
+ ret["error_code"] = error.code;
+ };
+
+ pending_events.push_back(ret);
+ }];
+};
+
+
+void GameCenter::request_achievements() {
+
+ [GKAchievement loadAchievementsWithCompletionHandler:^(NSArray *achievements, NSError *error) {
+
+ Dictionary ret;
+ ret["type"] = "achievements";
+ if (error == nil) {
+ ret["result"] = "ok";
+ StringArray names;
+ RealArray percentages;
+
+ for (int i=0; i<[achievements count]; i++) {
+
+ GKAchievement* achievement = [achievements objectAtIndex:i];
+ const char* str = [achievement.identifier UTF8String];
+ names.push_back(String::utf8(str != NULL ? str : ""));
+
+ percentages.push_back(achievement.percentComplete);
+ }
+
+ ret["names"] = names;
+ ret["progress"] = percentages;
+
+ } else {
+ ret["result"] = "error";
+ ret["error_code"] = error.code;
+ };
+
+ pending_events.push_back(ret);
+ }];
+};
+
+void GameCenter::reset_achievements() {
+
+ [GKAchievement resetAchievementsWithCompletionHandler:^(NSError *error)
+ {
+ Dictionary ret;
+ ret["type"] = "reset_achievements";
+ if (error == nil) {
+ ret["result"] = "ok";
+ } else {
+ ret["result"] = "error";
+ ret["error_code"] = error.code;
+ };
+
+ pending_events.push_back(ret);
+ }];
+};
+
+Error GameCenter::show_game_center(Variant p_params) {
+
+ ERR_FAIL_COND_V(!NSProtocolFromString(@"GKGameCenterControllerDelegate"), FAILED);
+
+ Dictionary params = p_params;
+
+ GKGameCenterViewControllerState view_state = GKGameCenterViewControllerStateDefault;
+ if (params.has("view")) {
+ String view_name = params["view"];
+ if (view_name == "default") {
+ view_state = GKGameCenterViewControllerStateDefault;
+ }
+ else if (view_name == "leaderboards") {
+ view_state = GKGameCenterViewControllerStateLeaderboards;
+ }
+ else if (view_name == "achievements") {
+ view_state = GKGameCenterViewControllerStateAchievements;
+ }
+ else if (view_name == "challenges") {
+ view_state = GKGameCenterViewControllerStateChallenges;
+ }
+ else {
+ return ERR_INVALID_PARAMETER;
+ }
+ }
+
+ GKGameCenterViewController *controller = [[GKGameCenterViewController alloc] init];
+ ERR_FAIL_COND_V(!controller, FAILED);
+
+ ViewController *root_controller=(ViewController *)((AppDelegate *)[[UIApplication sharedApplication] delegate]).window.rootViewController;
+ ERR_FAIL_COND_V(!root_controller, FAILED);
+
+ controller.gameCenterDelegate = root_controller;
+ controller.viewState = view_state;
+ if (view_state == GKGameCenterViewControllerStateLeaderboards) {
+ controller.leaderboardIdentifier = nil;
+ if (params.has("leaderboard_name")) {
+ String name = params["leaderboard_name"];
+ NSString* name_str = [[[NSString alloc] initWithUTF8String:name.utf8().get_data()] autorelease];
+ controller.leaderboardIdentifier = name_str;
+ }
+ }
+
+ [root_controller presentViewController: controller animated: YES completion:nil];
+
+ return OK;
+};
+
+void GameCenter::game_center_closed() {
+
+ Dictionary ret;
+ ret["type"] = "show_game_center";
+ ret["result"] = "ok";
+ pending_events.push_back(ret);
+}
+
int GameCenter::get_pending_event_count() {
return pending_events.size();
diff --git a/platform/iphone/gl_view.h b/platform/iphone/gl_view.h
index c58c863510..cda75394db 100755
--- a/platform/iphone/gl_view.h
+++ b/platform/iphone/gl_view.h
@@ -34,7 +34,7 @@
#import <MediaPlayer/MediaPlayer.h>
#import <AVFoundation/AVFoundation.h>
-#define USE_CADISPLAYLINK 1 //iOS version 3.1+ is required
+#define USE_CADISPLAYLINK 0 //iOS version 3.1+ is required
@protocol GLViewDelegate;
diff --git a/platform/iphone/gl_view.mm b/platform/iphone/gl_view.mm
index 55185aa49d..4d5d1b81e3 100755
--- a/platform/iphone/gl_view.mm
+++ b/platform/iphone/gl_view.mm
@@ -345,6 +345,8 @@ static void clear_touches() {
[self destroyFramebuffer];
[self createFramebuffer];
[self drawView];
+ [self drawView];
+
}
- (BOOL)createFramebuffer
@@ -352,8 +354,9 @@ static void clear_touches() {
// Generate IDs for a framebuffer object and a color renderbuffer
UIScreen* mainscr = [UIScreen mainScreen];
printf("******** screen size %i, %i\n", (int)mainscr.currentMode.size.width, (int)mainscr.currentMode.size.height);
- if (mainscr.currentMode.size.width == 640 || mainscr.currentMode.size.width == 960) // modern iphone, can go to 640x960
- self.contentScaleFactor = 2.0;
+ float minPointSize = MIN(mainscr.bounds.size.width, mainscr.bounds.size.height);
+ float minScreenSize = MIN(mainscr.currentMode.size.width, mainscr.currentMode.size.height);
+ self.contentScaleFactor = minScreenSize / minPointSize;
glGenFramebuffersOES(1, &viewFramebuffer);
glGenRenderbuffersOES(1, &viewRenderbuffer);
diff --git a/platform/iphone/icloud.h b/platform/iphone/icloud.h
new file mode 100644
index 0000000000..ca21f62ba1
--- /dev/null
+++ b/platform/iphone/icloud.h
@@ -0,0 +1,66 @@
+/*************************************************************************/
+/* icloud.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/*************************************************************************/
+#ifdef ICLOUD_ENABLED
+
+#ifndef ICLOUD_H
+#define ICLOUD_H
+
+#include "core/object.h"
+
+
+class ICloud : public Object {
+
+ OBJ_TYPE(ICloud, Object);
+
+ static ICloud* instance;
+ static void _bind_methods();
+
+ List<Variant> pending_events;
+
+public:
+
+ Error remove_key(Variant p_param);
+ Variant set_key_values(Variant p_param);
+ Variant get_key_value(Variant p_param);
+ Error synchronize_key_values();
+ Variant get_all_key_values();
+
+ int get_pending_event_count();
+ Variant pop_pending_event();
+
+ static ICloud* get_singleton();
+
+ ICloud();
+ ~ICloud();
+};
+
+
+#endif
+
+#endif
diff --git a/platform/iphone/icloud.mm b/platform/iphone/icloud.mm
new file mode 100644
index 0000000000..2dc2f7d9c1
--- /dev/null
+++ b/platform/iphone/icloud.mm
@@ -0,0 +1,379 @@
+/*************************************************************************/
+/* icloud.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/*************************************************************************/
+#ifdef ICLOUD_ENABLED
+
+#include "icloud.h"
+
+extern "C" {
+#import <Foundation/Foundation.h>
+#import "app_delegate.h"
+};
+
+ICloud* ICloud::instance = NULL;
+
+void ICloud::_bind_methods() {
+ ObjectTypeDB::bind_method(_MD("remove_key"),&ICloud::remove_key);
+ ObjectTypeDB::bind_method(_MD("set_key_values"),&ICloud::set_key_values);
+ ObjectTypeDB::bind_method(_MD("get_key_value"),&ICloud::get_key_value);
+ ObjectTypeDB::bind_method(_MD("synchronize_key_values"),&ICloud::synchronize_key_values);
+ ObjectTypeDB::bind_method(_MD("get_all_key_values"),&ICloud::get_all_key_values);
+
+ ObjectTypeDB::bind_method(_MD("get_pending_event_count"),&ICloud::get_pending_event_count);
+ ObjectTypeDB::bind_method(_MD("pop_pending_event"),&ICloud::pop_pending_event);
+};
+
+int ICloud::get_pending_event_count() {
+
+ return pending_events.size();
+};
+
+Variant ICloud::pop_pending_event() {
+
+ Variant front = pending_events.front()->get();
+ pending_events.pop_front();
+
+ return front;
+};
+
+ICloud* ICloud::get_singleton() {
+ return instance;
+};
+
+//convert from apple's abstract type to godot's abstract type....
+Variant nsobject_to_variant(NSObject* object) {
+ if ([object isKindOfClass:[NSString class]]) {
+ const char* str = [(NSString*)object UTF8String];
+ return String::utf8(str != NULL ? str : "");
+ }
+ else if ([object isKindOfClass:[NSData class]]) {
+ ByteArray ret;
+ NSData* data = (NSData*)object;
+ if ([data length] > 0) {
+ ret.resize([data length]);
+ {
+ ByteArray::Write w = ret.write();
+ copymem(w.ptr(), [data bytes], [data length]);
+ }
+ }
+ return ret;
+ }
+ else if ([object isKindOfClass:[NSArray class]]) {
+ Array result;
+ NSArray* array = (NSArray*)object;
+ for (unsigned int i = 0; i < [array count]; ++i) {
+ NSObject* value = [array objectAtIndex:i];
+ result.push_back(nsobject_to_variant(value));
+ }
+ return result;
+ }
+ else if ([object isKindOfClass:[NSDictionary class]]) {
+ Dictionary result;
+ NSDictionary* dic = (NSDictionary*)object;
+
+
+ NSArray* keys = [dic allKeys];
+ int count = [keys count];
+ for (int i=0; i < count; ++i) {
+ NSObject* k = [ keys objectAtIndex:i];
+ NSObject* v = [dic objectForKey:k];
+
+ result[nsobject_to_variant(k)] = nsobject_to_variant(v);
+ }
+ return result;
+ }
+ else if ([object isKindOfClass:[NSNumber class]]) {
+ //Every type except numbers can reliably identify its type. The following is comparing to the *internal* representation, which isn't guaranteed to match the type that was used to create it, and is not advised, particularly when dealing with potential platform differences (ie, 32/64 bit)
+ //To avoid errors, we'll cast as broadly as possible, and only return int or float.
+ //bool, char, int, uint, longlong -> int
+ //float, double -> float
+ NSNumber* num = (NSNumber*)object;
+ if(strcmp([num objCType], @encode(BOOL)) == 0) {
+ return Variant((int)[num boolValue]);
+ }
+ else if(strcmp([num objCType], @encode(char)) == 0) {
+ return Variant((int)[num charValue]);
+ }
+ else if(strcmp([num objCType], @encode(int)) == 0) {
+ return Variant([num intValue]);
+ }
+ else if(strcmp([num objCType], @encode(unsigned int)) == 0) {
+ return Variant((int)[num unsignedIntValue]);
+ }
+ else if(strcmp([num objCType], @encode(long long)) == 0) {
+ return Variant((int)[num longValue]);
+ }
+ else if(strcmp([num objCType], @encode(float)) == 0) {
+ return Variant([num floatValue]);
+ }
+ else if(strcmp([num objCType], @encode(double)) == 0) {
+ return Variant((float)[num doubleValue]);
+ }
+ }
+ else if ([object isKindOfClass:[NSDate class]]) {
+ //this is a type that icloud supports...but how did you submit it in the first place?
+ //I guess this is a type that *might* show up, if you were, say, trying to make your game
+ //compatible with existing cloud data written by another engine's version of your game
+ WARN_PRINT("NSDate unsupported, returning null Variant")
+ return Variant();
+ }
+ else if ([object isKindOfClass:[NSNull class]] or object == nil) {
+ return Variant();
+ }
+ else {
+ WARN_PRINT("Trying to convert unknown NSObject type to Variant");
+ return Variant();
+ }
+}
+
+NSObject* variant_to_nsobject(Variant v) {
+ if (v.get_type() == Variant::STRING) {
+ return [[[NSString alloc] initWithUTF8String:((String)v).utf8().get_data()] autorelease];
+ }
+ else if (v.get_type() == Variant::REAL) {
+ return [NSNumber numberWithDouble:(double)v];
+ }
+ else if (v.get_type() == Variant::INT) {
+ return [NSNumber numberWithLongLong:(long)(int)v];
+ }
+ else if (v.get_type() == Variant::BOOL) {
+ return [NSNumber numberWithBool:BOOL((bool)v)];
+ }
+ else if (v.get_type() == Variant::DICTIONARY) {
+ NSMutableDictionary* result = [[[NSMutableDictionary alloc] init] autorelease];
+ Dictionary dic = v;
+ Array keys = dic.keys();
+ for (unsigned int i = 0; i < keys.size(); ++i) {
+ NSString* key = [[[NSString alloc] initWithUTF8String:((String)(keys[i])).utf8().get_data()] autorelease];
+ NSObject* value = variant_to_nsobject(dic[keys[i]]);
+
+ if (key == NULL || value == NULL) {
+ return NULL;
+ }
+
+ [result setObject:value forKey:key];
+ }
+ return result;
+ }
+ else if (v.get_type() == Variant::ARRAY) {
+ NSMutableArray* result = [[[NSMutableArray alloc] init] autorelease];
+ Array arr = v;
+ for (unsigned int i = 0; i < arr.size(); ++i) {
+ NSObject* value = variant_to_nsobject(arr[i]);
+ if (value == NULL) {
+ //trying to add something unsupported to the array. cancel the whole array
+ return NULL;
+ }
+ [result addObject:value];
+ }
+ return result;
+ }
+ else if (v.get_type() == Variant::RAW_ARRAY) {
+ ByteArray arr = v;
+ ByteArray::Read r = arr.read();
+ NSData* result = [NSData dataWithBytes:r.ptr() length:arr.size()];
+ return result;
+ }
+ WARN_PRINT(String("Could not add unsupported type to iCloud: '" + Variant::get_type_name(v.get_type())+"'").utf8().get_data());
+ return NULL;
+}
+
+
+Error ICloud::remove_key(Variant p_param) {
+ String param = p_param;
+ NSString* key = [[[NSString alloc] initWithUTF8String:param.utf8().get_data()] autorelease];
+
+ NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
+
+ if (![[store dictionaryRepresentation] objectForKey:key]) {
+ return ERR_INVALID_PARAMETER;
+ }
+
+ [store removeObjectForKey:key];
+ return OK;
+}
+
+//return an array of the keys that could not be set
+Variant ICloud::set_key_values(Variant p_params) {
+ Dictionary params = p_params;
+ Array keys = params.keys();
+
+ Array error_keys;
+
+ for (unsigned int i = 0; i < keys.size(); ++i) {
+ String variant_key = keys[i];
+ Variant variant_value = params[variant_key];
+
+ NSString* key = [[[NSString alloc] initWithUTF8String:variant_key.utf8().get_data()] autorelease];
+ if (key == NULL) {
+ error_keys.push_back(variant_key);
+ continue;
+ }
+
+ NSObject* value = variant_to_nsobject(variant_value);
+
+ if (value == NULL) {
+ error_keys.push_back(variant_key);
+ continue;
+ }
+
+ NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
+ [store setObject:value forKey:key];
+ }
+
+ return error_keys;
+}
+
+Variant ICloud::get_key_value(Variant p_param) {
+ String param = p_param;
+
+ NSString* key = [[[NSString alloc] initWithUTF8String:param.utf8().get_data()] autorelease];
+ NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
+
+ if (![[store dictionaryRepresentation] objectForKey:key]) {
+ return Variant();
+ }
+
+ Variant result = nsobject_to_variant([[store dictionaryRepresentation] objectForKey:key]);
+
+ return result;
+}
+
+Variant ICloud::get_all_key_values() {
+ Dictionary result;
+
+ NSUbiquitousKeyValueStore* store = [NSUbiquitousKeyValueStore defaultStore];
+ NSDictionary* store_dictionary = [store dictionaryRepresentation];
+
+ NSArray* keys = [store_dictionary allKeys];
+ int count = [keys count];
+ for (int i=0; i < count; ++i) {
+ NSString* k = [ keys objectAtIndex:i];
+ NSObject* v = [store_dictionary objectForKey:k];
+
+ const char* str = [k UTF8String];
+ if (str != NULL) {
+ result[String::utf8(str)] = nsobject_to_variant(v);
+ }
+ }
+
+ return result;
+}
+
+Error ICloud::synchronize_key_values() {
+ NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
+ BOOL result = [store synchronize];
+ if (result == YES) {
+ return OK;
+ }
+ else {
+ return FAILED;
+ }
+}
+/*
+Error ICloud::initial_sync() {
+ //you sometimes have to write something to the store to get it to download new data. go apple!
+ NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
+ if ([store boolForKey:@"isb"])
+ {
+ [store setBool:NO forKey:@"isb"];
+ }
+ else
+ {
+ [store setBool:YES forKey:@"isb"];
+ }
+ return synchronize();
+}
+*/
+ICloud::ICloud() {
+ ERR_FAIL_COND(instance != NULL);
+ instance = this;
+ //connected = false;
+
+ [
+ //[NSNotificationCenter defaultCenter] addObserverForName: @"notify"
+ [NSNotificationCenter defaultCenter] addObserverForName: NSUbiquitousKeyValueStoreDidChangeExternallyNotification
+ object: [NSUbiquitousKeyValueStore defaultStore]
+ queue: nil
+ usingBlock: ^ (NSNotification * notification) {
+ NSDictionary* userInfo = [notification userInfo];
+ NSInteger change = [[userInfo objectForKey:NSUbiquitousKeyValueStoreChangeReasonKey] integerValue];
+
+ Dictionary ret;
+ ret["type"] = "key_value_changed";
+
+ //StringArray result_keys;
+ //Array result_values;
+ Dictionary keyValues;
+ String reason = "";
+
+ if (change == NSUbiquitousKeyValueStoreServerChange) {
+ reason = "server";
+ }
+ else if (change == NSUbiquitousKeyValueStoreInitialSyncChange) {
+ reason = "initial_sync";
+ }
+ else if (change == NSUbiquitousKeyValueStoreQuotaViolationChange) {
+ reason = "quota_violation";
+ }
+ else if (change == NSUbiquitousKeyValueStoreAccountChange) {
+ reason = "account";
+ }
+
+ ret["reason"] = reason;
+
+
+ NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
+
+ NSArray * keys = [userInfo objectForKey:NSUbiquitousKeyValueStoreChangedKeysKey];
+ for (NSString* key in keys) {
+ const char* str = [key UTF8String];
+ if (str == NULL) {
+ continue;
+ }
+
+ NSObject* object = [store objectForKey:key];
+
+ //figure out what kind of object it is
+ Variant value = nsobject_to_variant(object);
+
+ keyValues[String::utf8(str)] = value;
+ }
+
+ ret["changed_values"] = keyValues;
+ pending_events.push_back(ret);
+ }
+ ];
+}
+
+
+ICloud::~ICloud() {
+
+};
+
+#endif
diff --git a/platform/iphone/in_app_store.mm b/platform/iphone/in_app_store.mm
index 1d40b1762e..f689123c81 100644
--- a/platform/iphone/in_app_store.mm
+++ b/platform/iphone/in_app_store.mm
@@ -28,6 +28,10 @@
/*************************************************************************/
#ifdef STOREKIT_ENABLED
+#ifdef MODULE_FUSEBOXX_ENABLED
+#import "modules/fuseboxx/ios/FuseSDK.h"
+#endif
+
#include "in_app_store.h"
extern "C" {
@@ -210,7 +214,7 @@ Error InAppStore::request_product_info(Variant p_params) {
receipt_to_send = [receipt description];
}
Dictionary receipt_ret;
- receipt_ret["receipt"] = String::utf8([receipt_to_send UTF8String]);
+ receipt_ret["receipt"] = String::utf8(receipt_to_send != nil ? [receipt_to_send UTF8String] : "");
receipt_ret["sdk"] = sdk_version;
ret["receipt"] = receipt_ret;
@@ -222,6 +226,11 @@ Error InAppStore::request_product_info(Variant p_params) {
else{
[pending_transactions setObject:transaction forKey:transaction.payment.productIdentifier];
}
+
+ #ifdef MODULE_FUSEBOXX_ENABLED
+ printf("Registering transaction on Fuseboxx!\n");
+ [FuseSDK registerInAppPurchase: transaction];
+ #endif
} break;
case SKPaymentTransactionStateFailed: {
printf("status transaction failed!\n");
diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp
index bf85ecc9dd..93f4d00e05 100644
--- a/platform/iphone/os_iphone.cpp
+++ b/platform/iphone/os_iphone.cpp
@@ -136,7 +136,8 @@ void OSIPhone::initialize(const VideoMode& p_desired,int p_video_driver,int p_au
//
physics_server = memnew( PhysicsServerSW );
physics_server->init();
- physics_2d_server = memnew( Physics2DServerSW );
+ //physics_2d_server = memnew( Physics2DServerSW );
+ physics_2d_server = Physics2DServerWrapMT::init_server<Physics2DServerSW>();
physics_2d_server->init();
input = memnew( InputDefault );
@@ -159,6 +160,12 @@ void OSIPhone::initialize(const VideoMode& p_desired,int p_video_driver,int p_au
store_kit = memnew(InAppStore);
Globals::get_singleton()->add_singleton(Globals::Singleton("InAppStore", store_kit));
#endif
+
+#ifdef ICLOUD_ENABLED
+ icloud = memnew(ICloud);
+ Globals::get_singleton()->add_singleton(Globals::Singleton("ICloud", icloud));
+ //icloud->connect();
+#endif
};
MainLoop *OSIPhone::get_main_loop() const {
diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h
index 721db36f41..06a67b0cfd 100644
--- a/platform/iphone/os_iphone.h
+++ b/platform/iphone/os_iphone.h
@@ -38,12 +38,16 @@
#include "servers/visual/rasterizer.h"
#include "servers/physics/physics_server_sw.h"
#include "servers/physics_2d/physics_2d_server_sw.h"
+#include "servers/physics_2d/physics_2d_server_wrap_mt.h"
#include "servers/audio/audio_server_sw.h"
#include "servers/audio/sample_manager_sw.h"
#include "servers/spatial_sound/spatial_sound_server_sw.h"
#include "servers/spatial_sound_2d/spatial_sound_2d_server_sw.h"
+#include "main/input_default.h"
#include "game_center.h"
#include "in_app_store.h"
+#include "icloud.h"
+
class AudioDriverIphone;
class RasterizerGLES2;
@@ -87,6 +91,9 @@ private:
#ifdef STOREKIT_ENABLED
InAppStore* store_kit;
#endif
+#ifdef ICLOUD_ENABLED
+ ICloud* icloud;
+#endif
MainLoop *main_loop;
diff --git a/platform/iphone/view_controller.h b/platform/iphone/view_controller.h
index 9432aebd97..0cee2f6fbf 100644
--- a/platform/iphone/view_controller.h
+++ b/platform/iphone/view_controller.h
@@ -27,8 +27,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#import <UIKit/UIKit.h>
+#import <GameKit/GameKit.h>
-@interface ViewController : UIViewController {
+@interface ViewController : UIViewController <GKGameCenterControllerDelegate> {
};
diff --git a/platform/iphone/view_controller.mm b/platform/iphone/view_controller.mm
index a5ca689e61..6a9c3ac9ec 100644
--- a/platform/iphone/view_controller.mm
+++ b/platform/iphone/view_controller.mm
@@ -124,10 +124,17 @@ int add_cmdline(int p_argc, char** p_args) {
}
};
-
- (BOOL)prefersStatusBarHidden
{
return YES;
}
+#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