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

Fix iOS GameCenter connection flow
Add method for secure authentication to GameCenter

Evgeny Zuev 8 жил өмнө
parent
commit
fe02180feb

+ 6 - 3
platform/iphone/game_center.h

@@ -43,11 +43,13 @@ class GameCenter : public Object {
 
 	List<Variant> pending_events;
 
-	bool connected;
+	bool authenticated;
+
+	void return_connect_error(const char *p_error_description);
 
 public:
-	Error connect();
-	bool is_connected();
+	void connect();
+	bool is_authenticated();
 
 	Error post_score(Variant p_score);
 	Error award_achievement(Variant p_params);
@@ -55,6 +57,7 @@ public:
 	void request_achievements();
 	void request_achievement_descriptions();
 	Error show_game_center(Variant p_params);
+	Error request_identity_verification_signature();
 
 	void game_center_closed();
 

+ 56 - 14
platform/iphone/game_center.mm

@@ -49,8 +49,7 @@ extern "C" {
 GameCenter *GameCenter::instance = NULL;
 
 void GameCenter::_bind_methods() {
-	ClassDB::bind_method(D_METHOD("connect"), &GameCenter::connect);
-	ClassDB::bind_method(D_METHOD("is_connected"), &GameCenter::is_connected);
+	ClassDB::bind_method(D_METHOD("is_authenticated"), &GameCenter::is_authenticated);
 
 	ClassDB::bind_method(D_METHOD("post_score"), &GameCenter::post_score);
 	ClassDB::bind_method(D_METHOD("award_achievement"), &GameCenter::award_achievement);
@@ -58,24 +57,41 @@ void GameCenter::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("request_achievements"), &GameCenter::request_achievements);
 	ClassDB::bind_method(D_METHOD("request_achievement_descriptions"), &GameCenter::request_achievement_descriptions);
 	ClassDB::bind_method(D_METHOD("show_game_center"), &GameCenter::show_game_center);
+	ClassDB::bind_method(D_METHOD("request_identity_verification_signature"), &GameCenter::request_identity_verification_signature);
 
 	ClassDB::bind_method(D_METHOD("get_pending_event_count"), &GameCenter::get_pending_event_count);
 	ClassDB::bind_method(D_METHOD("pop_pending_event"), &GameCenter::pop_pending_event);
 };
 
-Error GameCenter::connect() {
+void GameCenter::return_connect_error(const char *p_error_description) {
+	authenticated = false;
+	Dictionary ret;
+	ret["type"] = "authentication";
+	ret["result"] = "error";
+	ret["error_code"] = 0;
+	ret["error_description"] = p_error_description;
+	pending_events.push_back(ret);
+}
+
+void GameCenter::connect() {
 
 	//if this class isn't available, game center isn't implemented
 	if ((NSClassFromString(@"GKLocalPlayer")) == nil) {
-		GameCenter::get_singleton()->connected = false;
-		return ERR_UNAVAILABLE;
+		return_connect_error("GameCenter not available");
+		return;
 	}
 
 	GKLocalPlayer *player = [GKLocalPlayer localPlayer];
-	ERR_FAIL_COND_V(![player respondsToSelector:@selector(authenticateHandler)], ERR_UNAVAILABLE);
+	if (![player respondsToSelector:@selector(authenticateHandler)]) {
+		return_connect_error("GameCenter doesn't respond to 'authenticateHandler'");
+		return;
+	}
 
 	ViewController *root_controller = (ViewController *)((AppDelegate *)[[UIApplication sharedApplication] delegate]).window.rootViewController;
-	ERR_FAIL_COND_V(!root_controller, FAILED);
+	if (!root_controller) {
+		return_connect_error("Window doesn't have root ViewController");
+		return;
+	}
 
 	// This handler is called several 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
@@ -90,23 +106,21 @@ Error GameCenter::connect() {
 			if (player.isAuthenticated) {
 				ret["result"] = "ok";
 				ret["player_id"] = [player.playerID UTF8String];
-				GameCenter::get_singleton()->connected = true;
+				GameCenter::get_singleton()->authenticated = true;
 			} else {
 				ret["result"] = "error";
 				ret["error_code"] = error.code;
 				ret["error_description"] = [error.localizedDescription UTF8String];
-				GameCenter::get_singleton()->connected = false;
+				GameCenter::get_singleton()->authenticated = false;
 			};
 
 			pending_events.push_back(ret);
 		};
 	});
-
-	return OK;
 };
 
-bool GameCenter::is_connected() {
-	return connected;
+bool GameCenter::is_authenticated() {
+	return authenticated;
 };
 
 Error GameCenter::post_score(Variant p_score) {
@@ -326,6 +340,34 @@ Error GameCenter::show_game_center(Variant p_params) {
 	return OK;
 };
 
+Error GameCenter::request_identity_verification_signature() {
+
+	ERR_FAIL_COND_V(!is_authenticated(), ERR_UNAUTHORIZED);
+
+	GKLocalPlayer *player = [GKLocalPlayer localPlayer];
+	[player generateIdentityVerificationSignatureWithCompletionHandler:^(NSURL *publicKeyUrl, NSData *signature, NSData *salt, uint64_t timestamp, NSError *error) {
+
+		Dictionary ret;
+		ret["type"] = "identity_verification_signature";
+		if (error == nil) {
+			ret["result"] = "ok";
+			ret["public_key_url"] = [publicKeyUrl.absoluteString UTF8String];
+			ret["signature"] = [[signature base64EncodedStringWithOptions:0] UTF8String];
+			ret["salt"] = [[salt base64EncodedStringWithOptions:0] UTF8String];
+			ret["timestamp"] = timestamp;
+			ret["player_id"] = [player.playerID UTF8String];
+		} else {
+			ret["result"] = "error";
+			ret["error_code"] = error.code;
+			ret["error_description"] = [error.localizedDescription UTF8String];
+		};
+
+		pending_events.push_back(ret);
+	}];
+
+	return OK;
+};
+
 void GameCenter::game_center_closed() {
 
 	Dictionary ret;
@@ -354,7 +396,7 @@ GameCenter *GameCenter::get_singleton() {
 GameCenter::GameCenter() {
 	ERR_FAIL_COND(instance != NULL);
 	instance = this;
-	connected = false;
+	authenticated = false;
 };
 
 GameCenter::~GameCenter(){};