game_center.mm 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. /*************************************************************************/
  2. /* game_center.mm */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /*************************************************************************/
  30. #ifdef GAME_CENTER_ENABLED
  31. #include "game_center.h"
  32. #ifdef __IPHONE_9_0
  33. #import <GameKit/GameKit.h>
  34. extern "C" {
  35. #else
  36. extern "C" {
  37. #import <GameKit/GameKit.h>
  38. #endif
  39. #import "app_delegate.h"
  40. };
  41. GameCenter *GameCenter::instance = NULL;
  42. void GameCenter::_bind_methods() {
  43. ClassDB::bind_method(D_METHOD("is_authenticated"), &GameCenter::is_authenticated);
  44. ClassDB::bind_method(D_METHOD("post_score"), &GameCenter::post_score);
  45. ClassDB::bind_method(D_METHOD("award_achievement"), &GameCenter::award_achievement);
  46. ClassDB::bind_method(D_METHOD("reset_achievements"), &GameCenter::reset_achievements);
  47. ClassDB::bind_method(D_METHOD("request_achievements"), &GameCenter::request_achievements);
  48. ClassDB::bind_method(D_METHOD("request_achievement_descriptions"), &GameCenter::request_achievement_descriptions);
  49. ClassDB::bind_method(D_METHOD("show_game_center"), &GameCenter::show_game_center);
  50. ClassDB::bind_method(D_METHOD("request_identity_verification_signature"), &GameCenter::request_identity_verification_signature);
  51. ClassDB::bind_method(D_METHOD("get_pending_event_count"), &GameCenter::get_pending_event_count);
  52. ClassDB::bind_method(D_METHOD("pop_pending_event"), &GameCenter::pop_pending_event);
  53. };
  54. void GameCenter::return_connect_error(const char *p_error_description) {
  55. authenticated = false;
  56. Dictionary ret;
  57. ret["type"] = "authentication";
  58. ret["result"] = "error";
  59. ret["error_code"] = 0;
  60. ret["error_description"] = p_error_description;
  61. pending_events.push_back(ret);
  62. }
  63. void GameCenter::connect() {
  64. //if this class isn't available, game center isn't implemented
  65. if ((NSClassFromString(@"GKLocalPlayer")) == nil) {
  66. return_connect_error("GameCenter not available");
  67. return;
  68. }
  69. GKLocalPlayer *player = [GKLocalPlayer localPlayer];
  70. if (![player respondsToSelector:@selector(authenticateHandler)]) {
  71. return_connect_error("GameCenter doesn't respond to 'authenticateHandler'");
  72. return;
  73. }
  74. ViewController *root_controller = (ViewController *)((AppDelegate *)[[UIApplication sharedApplication] delegate]).window.rootViewController;
  75. if (!root_controller) {
  76. return_connect_error("Window doesn't have root ViewController");
  77. return;
  78. }
  79. // This handler is called several times. First when the view needs to be shown, then again
  80. // after the view is cancelled or the user logs in. Or if the user's already logged in, it's
  81. // called just once to confirm they're authenticated. This is why no result needs to be specified
  82. // in the presentViewController phase. In this case, more calls to this function will follow.
  83. player.authenticateHandler = (^(UIViewController *controller, NSError *error) {
  84. if (controller) {
  85. [root_controller presentViewController:controller animated:YES completion:nil];
  86. } else {
  87. Dictionary ret;
  88. ret["type"] = "authentication";
  89. if (player.isAuthenticated) {
  90. ret["result"] = "ok";
  91. ret["player_id"] = [player.playerID UTF8String];
  92. GameCenter::get_singleton()->authenticated = true;
  93. } else {
  94. ret["result"] = "error";
  95. ret["error_code"] = (int64_t)error.code;
  96. ret["error_description"] = [error.localizedDescription UTF8String];
  97. GameCenter::get_singleton()->authenticated = false;
  98. };
  99. pending_events.push_back(ret);
  100. };
  101. });
  102. };
  103. bool GameCenter::is_authenticated() {
  104. return authenticated;
  105. };
  106. Error GameCenter::post_score(Variant p_score) {
  107. Dictionary params = p_score;
  108. ERR_FAIL_COND_V(!params.has("score") || !params.has("category"), ERR_INVALID_PARAMETER);
  109. float score = params["score"];
  110. String category = params["category"];
  111. NSString *cat_str = [[[NSString alloc] initWithUTF8String:category.utf8().get_data()] autorelease];
  112. GKScore *reporter = [[[GKScore alloc] initWithLeaderboardIdentifier:cat_str] autorelease];
  113. reporter.value = score;
  114. ERR_FAIL_COND_V([GKScore respondsToSelector:@selector(reportScores)], ERR_UNAVAILABLE);
  115. [GKScore reportScores:@[ reporter ]
  116. withCompletionHandler:^(NSError *error) {
  117. Dictionary ret;
  118. ret["type"] = "post_score";
  119. if (error == nil) {
  120. ret["result"] = "ok";
  121. } else {
  122. ret["result"] = "error";
  123. ret["error_code"] = (int64_t)error.code;
  124. ret["error_description"] = [error.localizedDescription UTF8String];
  125. };
  126. pending_events.push_back(ret);
  127. }];
  128. return OK;
  129. };
  130. Error GameCenter::award_achievement(Variant p_params) {
  131. Dictionary params = p_params;
  132. ERR_FAIL_COND_V(!params.has("name") || !params.has("progress"), ERR_INVALID_PARAMETER);
  133. String name = params["name"];
  134. float progress = params["progress"];
  135. NSString *name_str = [[[NSString alloc] initWithUTF8String:name.utf8().get_data()] autorelease];
  136. GKAchievement *achievement = [[[GKAchievement alloc] initWithIdentifier:name_str] autorelease];
  137. ERR_FAIL_COND_V(!achievement, FAILED);
  138. ERR_FAIL_COND_V([GKAchievement respondsToSelector:@selector(reportAchievements)], ERR_UNAVAILABLE);
  139. achievement.percentComplete = progress;
  140. achievement.showsCompletionBanner = NO;
  141. if (params.has("show_completion_banner")) {
  142. achievement.showsCompletionBanner = params["show_completion_banner"] ? YES : NO;
  143. }
  144. [GKAchievement reportAchievements:@[ achievement ]
  145. withCompletionHandler:^(NSError *error) {
  146. Dictionary ret;
  147. ret["type"] = "award_achievement";
  148. if (error == nil) {
  149. ret["result"] = "ok";
  150. } else {
  151. ret["result"] = "error";
  152. ret["error_code"] = (int64_t)error.code;
  153. };
  154. pending_events.push_back(ret);
  155. }];
  156. return OK;
  157. };
  158. void GameCenter::request_achievement_descriptions() {
  159. [GKAchievementDescription loadAchievementDescriptionsWithCompletionHandler:^(NSArray *descriptions, NSError *error) {
  160. Dictionary ret;
  161. ret["type"] = "achievement_descriptions";
  162. if (error == nil) {
  163. ret["result"] = "ok";
  164. PoolStringArray names;
  165. PoolStringArray titles;
  166. PoolStringArray unachieved_descriptions;
  167. PoolStringArray achieved_descriptions;
  168. PoolIntArray maximum_points;
  169. Array hidden;
  170. Array replayable;
  171. for (int i = 0; i < [descriptions count]; i++) {
  172. GKAchievementDescription *description = [descriptions objectAtIndex:i];
  173. const char *str = [description.identifier UTF8String];
  174. names.push_back(String::utf8(str != NULL ? str : ""));
  175. str = [description.title UTF8String];
  176. titles.push_back(String::utf8(str != NULL ? str : ""));
  177. str = [description.unachievedDescription UTF8String];
  178. unachieved_descriptions.push_back(String::utf8(str != NULL ? str : ""));
  179. str = [description.achievedDescription UTF8String];
  180. achieved_descriptions.push_back(String::utf8(str != NULL ? str : ""));
  181. maximum_points.push_back(description.maximumPoints);
  182. hidden.push_back(description.hidden == YES);
  183. replayable.push_back(description.replayable == YES);
  184. }
  185. ret["names"] = names;
  186. ret["titles"] = titles;
  187. ret["unachieved_descriptions"] = unachieved_descriptions;
  188. ret["achieved_descriptions"] = achieved_descriptions;
  189. ret["maximum_points"] = maximum_points;
  190. ret["hidden"] = hidden;
  191. ret["replayable"] = replayable;
  192. } else {
  193. ret["result"] = "error";
  194. ret["error_code"] = (int64_t)error.code;
  195. };
  196. pending_events.push_back(ret);
  197. }];
  198. };
  199. void GameCenter::request_achievements() {
  200. [GKAchievement loadAchievementsWithCompletionHandler:^(NSArray *achievements, NSError *error) {
  201. Dictionary ret;
  202. ret["type"] = "achievements";
  203. if (error == nil) {
  204. ret["result"] = "ok";
  205. PoolStringArray names;
  206. PoolRealArray percentages;
  207. for (int i = 0; i < [achievements count]; i++) {
  208. GKAchievement *achievement = [achievements objectAtIndex:i];
  209. const char *str = [achievement.identifier UTF8String];
  210. names.push_back(String::utf8(str != NULL ? str : ""));
  211. percentages.push_back(achievement.percentComplete);
  212. }
  213. ret["names"] = names;
  214. ret["progress"] = percentages;
  215. } else {
  216. ret["result"] = "error";
  217. ret["error_code"] = (int64_t)error.code;
  218. };
  219. pending_events.push_back(ret);
  220. }];
  221. };
  222. void GameCenter::reset_achievements() {
  223. [GKAchievement resetAchievementsWithCompletionHandler:^(NSError *error) {
  224. Dictionary ret;
  225. ret["type"] = "reset_achievements";
  226. if (error == nil) {
  227. ret["result"] = "ok";
  228. } else {
  229. ret["result"] = "error";
  230. ret["error_code"] = (int64_t)error.code;
  231. };
  232. pending_events.push_back(ret);
  233. }];
  234. };
  235. Error GameCenter::show_game_center(Variant p_params) {
  236. ERR_FAIL_COND_V(!NSProtocolFromString(@"GKGameCenterControllerDelegate"), FAILED);
  237. Dictionary params = p_params;
  238. GKGameCenterViewControllerState view_state = GKGameCenterViewControllerStateDefault;
  239. if (params.has("view")) {
  240. String view_name = params["view"];
  241. if (view_name == "default") {
  242. view_state = GKGameCenterViewControllerStateDefault;
  243. } else if (view_name == "leaderboards") {
  244. view_state = GKGameCenterViewControllerStateLeaderboards;
  245. } else if (view_name == "achievements") {
  246. view_state = GKGameCenterViewControllerStateAchievements;
  247. } else if (view_name == "challenges") {
  248. view_state = GKGameCenterViewControllerStateChallenges;
  249. } else {
  250. return ERR_INVALID_PARAMETER;
  251. }
  252. }
  253. GKGameCenterViewController *controller = [[GKGameCenterViewController alloc] init];
  254. ERR_FAIL_COND_V(!controller, FAILED);
  255. ViewController *root_controller = (ViewController *)((AppDelegate *)[[UIApplication sharedApplication] delegate]).window.rootViewController;
  256. ERR_FAIL_COND_V(!root_controller, FAILED);
  257. controller.gameCenterDelegate = root_controller;
  258. controller.viewState = view_state;
  259. if (view_state == GKGameCenterViewControllerStateLeaderboards) {
  260. controller.leaderboardIdentifier = nil;
  261. if (params.has("leaderboard_name")) {
  262. String name = params["leaderboard_name"];
  263. NSString *name_str = [[[NSString alloc] initWithUTF8String:name.utf8().get_data()] autorelease];
  264. controller.leaderboardIdentifier = name_str;
  265. }
  266. }
  267. [root_controller presentViewController:controller animated:YES completion:nil];
  268. return OK;
  269. };
  270. Error GameCenter::request_identity_verification_signature() {
  271. ERR_FAIL_COND_V(!is_authenticated(), ERR_UNAUTHORIZED);
  272. GKLocalPlayer *player = [GKLocalPlayer localPlayer];
  273. [player generateIdentityVerificationSignatureWithCompletionHandler:^(NSURL *publicKeyUrl, NSData *signature, NSData *salt, uint64_t timestamp, NSError *error) {
  274. Dictionary ret;
  275. ret["type"] = "identity_verification_signature";
  276. if (error == nil) {
  277. ret["result"] = "ok";
  278. ret["public_key_url"] = [publicKeyUrl.absoluteString UTF8String];
  279. ret["signature"] = [[signature base64EncodedStringWithOptions:0] UTF8String];
  280. ret["salt"] = [[salt base64EncodedStringWithOptions:0] UTF8String];
  281. ret["timestamp"] = timestamp;
  282. ret["player_id"] = [player.playerID UTF8String];
  283. } else {
  284. ret["result"] = "error";
  285. ret["error_code"] = (int64_t)error.code;
  286. ret["error_description"] = [error.localizedDescription UTF8String];
  287. };
  288. pending_events.push_back(ret);
  289. }];
  290. return OK;
  291. };
  292. void GameCenter::game_center_closed() {
  293. Dictionary ret;
  294. ret["type"] = "show_game_center";
  295. ret["result"] = "ok";
  296. pending_events.push_back(ret);
  297. }
  298. int GameCenter::get_pending_event_count() {
  299. return pending_events.size();
  300. };
  301. Variant GameCenter::pop_pending_event() {
  302. Variant front = pending_events.front()->get();
  303. pending_events.pop_front();
  304. return front;
  305. };
  306. GameCenter *GameCenter::get_singleton() {
  307. return instance;
  308. };
  309. GameCenter::GameCenter() {
  310. ERR_FAIL_COND(instance != NULL);
  311. instance = this;
  312. authenticated = false;
  313. };
  314. GameCenter::~GameCenter(){};
  315. #endif