diff options
-rw-r--r-- | platform/iphone/SCsub | 1 | ||||
-rw-r--r-- | platform/iphone/detect.py | 4 | ||||
-rw-r--r-- | platform/iphone/icloud.h | 66 | ||||
-rw-r--r-- | platform/iphone/icloud.mm | 379 | ||||
-rw-r--r-- | platform/iphone/os_iphone.cpp | 6 | ||||
-rw-r--r-- | platform/iphone/os_iphone.h | 4 | ||||
-rw-r--r-- | platform/iphone/view_controller.mm | 2 |
7 files changed, 462 insertions, 0 deletions
diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub index d755b3dba0..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', ] diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py index 3864968d94..da7e86acf5 100644 --- a/platform/iphone/detect.py +++ b/platform/iphone/detect.py @@ -26,6 +26,7 @@ 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 backups', 'yes'), ('ios_gles22_override', 'Force GLES2.0 on iOS', 'yes'), ('ios_appirater', 'Enable Appirater', 'no'), ('ios_exceptions', 'Use exceptions when compiling on playbook', 'yes'), @@ -108,6 +109,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']) 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/os_iphone.cpp b/platform/iphone/os_iphone.cpp index ade1c292a4..93f4d00e05 100644 --- a/platform/iphone/os_iphone.cpp +++ b/platform/iphone/os_iphone.cpp @@ -160,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 844f067552..de000d0bf7 100644 --- a/platform/iphone/os_iphone.h +++ b/platform/iphone/os_iphone.h @@ -45,6 +45,7 @@ #include "servers/spatial_sound_2d/spatial_sound_2d_server_sw.h" #include "game_center.h" #include "in_app_store.h" +#include "icloud.h" class AudioDriverIphone; class RasterizerGLES2; @@ -88,6 +89,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.mm b/platform/iphone/view_controller.mm index bc9950979e..6a9c3ac9ec 100644 --- a/platform/iphone/view_controller.mm +++ b/platform/iphone/view_controller.mm @@ -129,10 +129,12 @@ int add_cmdline(int p_argc, char** p_args) { 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 |