Explorar o código

Merge pull request #2548 from romulox-x/iCloud

Initial iCloud implementation
punto- %!s(int64=10) %!d(string=hai) anos
pai
achega
0840303a9c

+ 1 - 0
platform/iphone/SCsub

@@ -12,6 +12,7 @@ iphone_lib = [
 	'view_controller.mm',
 	'view_controller.mm',
 	'game_center.mm',
 	'game_center.mm',
 	'in_app_store.mm',
 	'in_app_store.mm',
+	'icloud.mm',
 	'Appirater.m',
 	'Appirater.m',
 ]
 ]
 
 

+ 4 - 0
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/'),
 		('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'),
 		('game_center', 'Support for game center', 'yes'),
 		('store_kit', 'Support for in-app store', 'yes'),
 		('store_kit', 'Support for in-app store', 'yes'),
+		('icloud', 'Support for iCloud', 'yes'),
 		('ios_gles22_override', 'Force GLES2.0 on iOS', 'yes'),
 		('ios_gles22_override', 'Force GLES2.0 on iOS', 'yes'),
 		('ios_appirater', 'Enable Appirater', 'no'),
 		('ios_appirater', 'Enable Appirater', 'no'),
 		('ios_exceptions', 'Use exceptions when compiling on playbook', 'yes'),
 		('ios_exceptions', 'Use exceptions when compiling on playbook', 'yes'),
@@ -108,6 +109,9 @@ def configure(env):
 	if env['store_kit'] == 'yes':
 	if env['store_kit'] == 'yes':
 		env.Append(CPPFLAGS=['-DSTOREKIT_ENABLED'])
 		env.Append(CPPFLAGS=['-DSTOREKIT_ENABLED'])
 		env.Append(LINKFLAGS=['-framework', 'StoreKit'])
 		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'])
 	env.Append(CPPPATH = ['$IPHONESDK/usr/include', '$IPHONESDK/System/Library/Frameworks/OpenGLES.framework/Headers', '$IPHONESDK/System/Library/Frameworks/AudioUnit.framework/Headers'])
 
 

+ 66 - 0
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

+ 379 - 0
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

+ 6 - 0
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);
 	store_kit = memnew(InAppStore);
 	Globals::get_singleton()->add_singleton(Globals::Singleton("InAppStore", store_kit));
 	Globals::get_singleton()->add_singleton(Globals::Singleton("InAppStore", store_kit));
 #endif		
 #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 {
 MainLoop *OSIPhone::get_main_loop() const {

+ 4 - 0
platform/iphone/os_iphone.h

@@ -46,6 +46,7 @@
 #include "main/input_default.h"
 #include "main/input_default.h"
 #include "game_center.h"
 #include "game_center.h"
 #include "in_app_store.h"
 #include "in_app_store.h"
+#include "icloud.h"
 
 
 
 
 class AudioDriverIphone;
 class AudioDriverIphone;
@@ -90,6 +91,9 @@ private:
 #ifdef STOREKIT_ENABLED
 #ifdef STOREKIT_ENABLED
 	InAppStore* store_kit;
 	InAppStore* store_kit;
 #endif
 #endif
+#ifdef ICLOUD_ENABLED
+	ICloud* icloud;
+#endif
 
 
 	MainLoop *main_loop;
 	MainLoop *main_loop;
 
 

+ 2 - 0
platform/iphone/view_controller.mm

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