mirror of
https://github.com/godotengine/godot.git
synced 2026-01-06 10:11:57 +03:00
Merge pull request #41340 from naithar/feature/pluggable-ios-modules-3.2
[iOS] [3.2] iOS Plugins
This commit is contained in:
18
modules/arkit/arkit.gdip
Normal file
18
modules/arkit/arkit.gdip
Normal file
@@ -0,0 +1,18 @@
|
||||
[config]
|
||||
name="ARKit"
|
||||
binary="arkit_lib.a"
|
||||
|
||||
initialization="register_arkit_types"
|
||||
deinitialization="unregister_arkit_types"
|
||||
|
||||
[dependencies]
|
||||
linked=[]
|
||||
embedded=[]
|
||||
system=["AVFoundation.framework", "ARKit.framework"]
|
||||
|
||||
capabilities=["arkit"]
|
||||
|
||||
files=[]
|
||||
|
||||
[plist]
|
||||
NSCameraUsageDescription="Device camera is used for some functionality"
|
||||
@@ -1,5 +1,5 @@
|
||||
/*************************************************************************/
|
||||
/* register_types.cpp */
|
||||
/* arkit_module.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
@@ -28,7 +28,7 @@
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "register_types.h"
|
||||
#include "arkit_module.h"
|
||||
|
||||
#include "arkit_interface.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*************************************************************************/
|
||||
/* register_types.h */
|
||||
/* arkit_module.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
@@ -5,17 +5,7 @@ Import("env_modules")
|
||||
|
||||
env_camera = env_modules.Clone()
|
||||
|
||||
if env["platform"] == "iphone":
|
||||
# (iOS) Enable module support
|
||||
env_camera.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
|
||||
|
||||
# (iOS) Build as separate static library
|
||||
modules_sources = []
|
||||
env_camera.add_source_files(modules_sources, "register_types.cpp")
|
||||
env_camera.add_source_files(modules_sources, "camera_ios.mm")
|
||||
mod_lib = env_modules.add_library("#bin/libgodot_camera_module" + env["LIBSUFFIX"], modules_sources)
|
||||
|
||||
elif env["platform"] == "windows":
|
||||
if env["platform"] == "windows":
|
||||
env_camera.add_source_files(env.modules_sources, "register_types.cpp")
|
||||
env_camera.add_source_files(env.modules_sources, "camera_win.cpp")
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
def can_build(env, platform):
|
||||
return platform == "iphone" or platform == "osx" or platform == "windows"
|
||||
return platform == "osx" or platform == "windows"
|
||||
|
||||
|
||||
def configure(env):
|
||||
|
||||
@@ -33,9 +33,6 @@
|
||||
#if defined(WINDOWS_ENABLED)
|
||||
#include "camera_win.h"
|
||||
#endif
|
||||
#if defined(IPHONE_ENABLED)
|
||||
#include "camera_ios.h"
|
||||
#endif
|
||||
#if defined(OSX_ENABLED)
|
||||
#include "camera_osx.h"
|
||||
#endif
|
||||
@@ -44,9 +41,6 @@ void register_camera_types() {
|
||||
#if defined(WINDOWS_ENABLED)
|
||||
CameraServer::make_default<CameraWindows>();
|
||||
#endif
|
||||
#if defined(IPHONE_ENABLED)
|
||||
CameraServer::make_default<CameraIOS>();
|
||||
#endif
|
||||
#if defined(OSX_ENABLED)
|
||||
CameraServer::make_default<CameraOSX>();
|
||||
#endif
|
||||
|
||||
15
modules/camera_iphone/SCsub
Normal file
15
modules/camera_iphone/SCsub
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
Import("env_modules")
|
||||
|
||||
env_camera = env_modules.Clone()
|
||||
|
||||
# (iOS) Enable module support
|
||||
env_camera.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
|
||||
|
||||
# (iOS) Build as separate static library
|
||||
modules_sources = []
|
||||
env_camera.add_source_files(modules_sources, "*.cpp")
|
||||
env_camera.add_source_files(modules_sources, "*.mm")
|
||||
mod_lib = env_modules.add_library("#bin/libgodot_camera_module" + env["LIBSUFFIX"], modules_sources)
|
||||
18
modules/camera_iphone/camera.gdip
Normal file
18
modules/camera_iphone/camera.gdip
Normal file
@@ -0,0 +1,18 @@
|
||||
[config]
|
||||
name="Camera"
|
||||
binary="camera_lib.a"
|
||||
|
||||
initialization="register_camera_types"
|
||||
deinitialization="unregister_camera_types"
|
||||
|
||||
[dependencies]
|
||||
linked=[]
|
||||
embedded=[]
|
||||
system=["AVFoundation.framework"]
|
||||
|
||||
capabilities=[]
|
||||
|
||||
files=[]
|
||||
|
||||
[plist]
|
||||
NSCameraUsageDescription="Device camera is used for some functionality"
|
||||
40
modules/camera_iphone/camera_module.cpp
Normal file
40
modules/camera_iphone/camera_module.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
/*************************************************************************/
|
||||
/* camera_module.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "camera_module.h"
|
||||
|
||||
#include "camera_ios.h"
|
||||
|
||||
void register_camera_types() {
|
||||
CameraServer::make_default<CameraIOS>();
|
||||
}
|
||||
|
||||
void unregister_camera_types() {
|
||||
}
|
||||
32
modules/camera_iphone/camera_module.h
Normal file
32
modules/camera_iphone/camera_module.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*************************************************************************/
|
||||
/* camera_module.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
void register_camera_types();
|
||||
void unregister_camera_types();
|
||||
6
modules/camera_iphone/config.py
Normal file
6
modules/camera_iphone/config.py
Normal file
@@ -0,0 +1,6 @@
|
||||
def can_build(env, platform):
|
||||
return platform == "iphone"
|
||||
|
||||
|
||||
def configure(env):
|
||||
pass
|
||||
15
modules/gamecenter/SCsub
Normal file
15
modules/gamecenter/SCsub
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
Import("env_modules")
|
||||
|
||||
env_gamecenter = env_modules.Clone()
|
||||
|
||||
# (iOS) Enable module support
|
||||
env_gamecenter.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
|
||||
|
||||
# (iOS) Build as separate static library
|
||||
modules_sources = []
|
||||
env_gamecenter.add_source_files(modules_sources, "*.cpp")
|
||||
env_gamecenter.add_source_files(modules_sources, "*.mm")
|
||||
mod_lib = env_modules.add_library("#bin/libgodot_gamecenter_module" + env["LIBSUFFIX"], modules_sources)
|
||||
6
modules/gamecenter/config.py
Normal file
6
modules/gamecenter/config.py
Normal file
@@ -0,0 +1,6 @@
|
||||
def can_build(env, platform):
|
||||
return platform == "iphone"
|
||||
|
||||
|
||||
def configure(env):
|
||||
pass
|
||||
72
modules/gamecenter/game_center.h
Normal file
72
modules/gamecenter/game_center.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/*************************************************************************/
|
||||
/* game_center.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef GAME_CENTER_H
|
||||
#define GAME_CENTER_H
|
||||
|
||||
#include "core/object.h"
|
||||
|
||||
class GameCenter : public Object {
|
||||
|
||||
GDCLASS(GameCenter, Object);
|
||||
|
||||
static GameCenter *instance;
|
||||
static void _bind_methods();
|
||||
|
||||
List<Variant> pending_events;
|
||||
|
||||
bool authenticated;
|
||||
|
||||
void return_connect_error(const char *p_error_description);
|
||||
|
||||
public:
|
||||
Error authenticate();
|
||||
bool is_authenticated();
|
||||
|
||||
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);
|
||||
Error request_identity_verification_signature();
|
||||
|
||||
void game_center_closed();
|
||||
|
||||
int get_pending_event_count();
|
||||
Variant pop_pending_event();
|
||||
|
||||
static GameCenter *get_singleton();
|
||||
|
||||
GameCenter();
|
||||
~GameCenter();
|
||||
};
|
||||
|
||||
#endif
|
||||
385
modules/gamecenter/game_center.mm
Normal file
385
modules/gamecenter/game_center.mm
Normal file
@@ -0,0 +1,385 @@
|
||||
/*************************************************************************/
|
||||
/* game_center.mm */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "game_center.h"
|
||||
|
||||
#import "game_center_delegate.h"
|
||||
#import "platform/iphone/app_delegate.h"
|
||||
#import "platform/iphone/view_controller.h"
|
||||
|
||||
#import <GameKit/GameKit.h>
|
||||
|
||||
GameCenter *GameCenter::instance = NULL;
|
||||
GodotGameCenterDelegate *gameCenterDelegate = nil;
|
||||
|
||||
void GameCenter::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("authenticate"), &GameCenter::authenticate);
|
||||
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);
|
||||
ClassDB::bind_method(D_METHOD("reset_achievements"), &GameCenter::reset_achievements);
|
||||
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::authenticate() {
|
||||
|
||||
//if this class isn't available, game center isn't implemented
|
||||
if ((NSClassFromString(@"GKLocalPlayer")) == nil) {
|
||||
return ERR_UNAVAILABLE;
|
||||
}
|
||||
|
||||
GKLocalPlayer *player = [GKLocalPlayer localPlayer];
|
||||
ERR_FAIL_COND_V(![player respondsToSelector:@selector(authenticateHandler)], ERR_UNAVAILABLE);
|
||||
|
||||
UIViewController *root_controller = [[UIApplication sharedApplication] delegate].window.rootViewController;
|
||||
ERR_FAIL_COND_V(!root_controller, FAILED);
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
_weakify(root_controller);
|
||||
_weakify(player);
|
||||
player.authenticateHandler = (^(UIViewController *controller, NSError *error) {
|
||||
_strongify(root_controller);
|
||||
_strongify(player);
|
||||
|
||||
if (controller) {
|
||||
[root_controller presentViewController:controller animated:YES completion:nil];
|
||||
} else {
|
||||
Dictionary ret;
|
||||
ret["type"] = "authentication";
|
||||
if (player.isAuthenticated) {
|
||||
ret["result"] = "ok";
|
||||
ret["player_id"] = [player.playerID UTF8String];
|
||||
GameCenter::get_singleton()->authenticated = true;
|
||||
} else {
|
||||
ret["result"] = "error";
|
||||
ret["error_code"] = (int64_t)error.code;
|
||||
ret["error_description"] = [error.localizedDescription UTF8String];
|
||||
GameCenter::get_singleton()->authenticated = false;
|
||||
};
|
||||
|
||||
pending_events.push_back(ret);
|
||||
};
|
||||
});
|
||||
|
||||
return OK;
|
||||
};
|
||||
|
||||
bool GameCenter::is_authenticated() {
|
||||
return authenticated;
|
||||
};
|
||||
|
||||
Error GameCenter::post_score(Variant p_score) {
|
||||
|
||||
Dictionary params = p_score;
|
||||
ERR_FAIL_COND_V(!params.has("score") || !params.has("category"), ERR_INVALID_PARAMETER);
|
||||
float score = params["score"];
|
||||
String category = params["category"];
|
||||
|
||||
NSString *cat_str = [[NSString alloc] initWithUTF8String:category.utf8().get_data()];
|
||||
GKScore *reporter = [[GKScore alloc] initWithLeaderboardIdentifier:cat_str];
|
||||
reporter.value = score;
|
||||
|
||||
ERR_FAIL_COND_V([GKScore respondsToSelector:@selector(reportScores)], ERR_UNAVAILABLE);
|
||||
|
||||
[GKScore reportScores:@[ reporter ]
|
||||
withCompletionHandler:^(NSError *error) {
|
||||
Dictionary ret;
|
||||
ret["type"] = "post_score";
|
||||
if (error == nil) {
|
||||
ret["result"] = "ok";
|
||||
} else {
|
||||
ret["result"] = "error";
|
||||
ret["error_code"] = (int64_t)error.code;
|
||||
ret["error_description"] = [error.localizedDescription UTF8String];
|
||||
};
|
||||
|
||||
pending_events.push_back(ret);
|
||||
}];
|
||||
|
||||
return OK;
|
||||
};
|
||||
|
||||
Error GameCenter::award_achievement(Variant p_params) {
|
||||
|
||||
Dictionary params = p_params;
|
||||
ERR_FAIL_COND_V(!params.has("name") || !params.has("progress"), ERR_INVALID_PARAMETER);
|
||||
String name = params["name"];
|
||||
float progress = params["progress"];
|
||||
|
||||
NSString *name_str = [[NSString alloc] initWithUTF8String:name.utf8().get_data()];
|
||||
GKAchievement *achievement = [[GKAchievement alloc] initWithIdentifier:name_str];
|
||||
ERR_FAIL_COND_V(!achievement, FAILED);
|
||||
|
||||
ERR_FAIL_COND_V([GKAchievement respondsToSelector:@selector(reportAchievements)], ERR_UNAVAILABLE);
|
||||
|
||||
achievement.percentComplete = progress;
|
||||
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";
|
||||
if (error == nil) {
|
||||
ret["result"] = "ok";
|
||||
} else {
|
||||
ret["result"] = "error";
|
||||
ret["error_code"] = (int64_t)error.code;
|
||||
};
|
||||
|
||||
pending_events.push_back(ret);
|
||||
}];
|
||||
|
||||
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";
|
||||
PoolStringArray names;
|
||||
PoolStringArray titles;
|
||||
PoolStringArray unachieved_descriptions;
|
||||
PoolStringArray achieved_descriptions;
|
||||
PoolIntArray maximum_points;
|
||||
Array hidden;
|
||||
Array replayable;
|
||||
|
||||
for (NSUInteger 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"] = (int64_t)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";
|
||||
PoolStringArray names;
|
||||
PoolRealArray percentages;
|
||||
|
||||
for (NSUInteger 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"] = (int64_t)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"] = (int64_t)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);
|
||||
|
||||
UIViewController *root_controller = [[UIApplication sharedApplication] delegate].window.rootViewController;
|
||||
ERR_FAIL_COND_V(!root_controller, FAILED);
|
||||
|
||||
controller.gameCenterDelegate = gameCenterDelegate;
|
||||
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()];
|
||||
controller.leaderboardIdentifier = name_str;
|
||||
}
|
||||
}
|
||||
|
||||
[root_controller presentViewController:controller animated:YES completion:nil];
|
||||
|
||||
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"] = (int64_t)error.code;
|
||||
ret["error_description"] = [error.localizedDescription UTF8String];
|
||||
};
|
||||
|
||||
pending_events.push_back(ret);
|
||||
}];
|
||||
|
||||
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();
|
||||
};
|
||||
|
||||
Variant GameCenter::pop_pending_event() {
|
||||
|
||||
Variant front = pending_events.front()->get();
|
||||
pending_events.pop_front();
|
||||
|
||||
return front;
|
||||
};
|
||||
|
||||
GameCenter *GameCenter::get_singleton() {
|
||||
return instance;
|
||||
};
|
||||
|
||||
GameCenter::GameCenter() {
|
||||
ERR_FAIL_COND(instance != NULL);
|
||||
instance = this;
|
||||
authenticated = false;
|
||||
|
||||
gameCenterDelegate = [[GodotGameCenterDelegate alloc] init];
|
||||
};
|
||||
|
||||
GameCenter::~GameCenter() {
|
||||
if (gameCenterDelegate) {
|
||||
gameCenterDelegate = nil;
|
||||
}
|
||||
}
|
||||
35
modules/gamecenter/game_center_delegate.h
Normal file
35
modules/gamecenter/game_center_delegate.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*************************************************************************/
|
||||
/* game_center_delegate.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
#import <GameKit/GameKit.h>
|
||||
|
||||
@interface GodotGameCenterDelegate : NSObject <GKGameCenterControllerDelegate>
|
||||
|
||||
@end
|
||||
45
modules/gamecenter/game_center_delegate.mm
Normal file
45
modules/gamecenter/game_center_delegate.mm
Normal file
@@ -0,0 +1,45 @@
|
||||
/*************************************************************************/
|
||||
/* game_center_delegate.mm */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
#import "game_center_delegate.h"
|
||||
|
||||
#include "game_center.h"
|
||||
|
||||
@implementation GodotGameCenterDelegate
|
||||
|
||||
- (void)gameCenterViewControllerDidFinish:(GKGameCenterViewController *)gameCenterViewController {
|
||||
//[gameCenterViewController dismissViewControllerAnimated:YES completion:^{GameCenter::get_singleton()->game_center_closed();}];//version for signaling when overlay is completely gone
|
||||
if (GameCenter::get_singleton()) {
|
||||
GameCenter::get_singleton()->game_center_closed();
|
||||
}
|
||||
[gameCenterViewController dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
48
modules/gamecenter/game_center_module.cpp
Normal file
48
modules/gamecenter/game_center_module.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/*************************************************************************/
|
||||
/* game_center_module.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "game_center_module.h"
|
||||
|
||||
#include "core/engine.h"
|
||||
|
||||
#include "game_center.h"
|
||||
|
||||
GameCenter *game_center;
|
||||
|
||||
void register_gamecenter_types() {
|
||||
game_center = memnew(GameCenter);
|
||||
Engine::get_singleton()->add_singleton(Engine::Singleton("GameCenter", game_center));
|
||||
}
|
||||
|
||||
void unregister_gamecenter_types() {
|
||||
if (game_center) {
|
||||
memdelete(game_center);
|
||||
}
|
||||
}
|
||||
32
modules/gamecenter/game_center_module.h
Normal file
32
modules/gamecenter/game_center_module.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*************************************************************************/
|
||||
/* game_center_module.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
void register_gamecenter_types();
|
||||
void unregister_gamecenter_types();
|
||||
17
modules/gamecenter/gamecenter.gdip
Normal file
17
modules/gamecenter/gamecenter.gdip
Normal file
@@ -0,0 +1,17 @@
|
||||
[config]
|
||||
name="GameCenter"
|
||||
binary="gamecenter_lib.a"
|
||||
|
||||
initialization="register_gamecenter_types"
|
||||
deinitialization="unregister_gamecenter_types"
|
||||
|
||||
[dependencies]
|
||||
linked=[]
|
||||
embedded=[]
|
||||
system=["GameKit.framework"]
|
||||
|
||||
capabilities=["gamekit"]
|
||||
|
||||
files=[]
|
||||
|
||||
[plist]
|
||||
15
modules/icloud/SCsub
Normal file
15
modules/icloud/SCsub
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
Import("env_modules")
|
||||
|
||||
env_icloud = env_modules.Clone()
|
||||
|
||||
# (iOS) Enable module support
|
||||
env_icloud.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
|
||||
|
||||
# (iOS) Build as separate static library
|
||||
modules_sources = []
|
||||
env_icloud.add_source_files(modules_sources, "*.cpp")
|
||||
env_icloud.add_source_files(modules_sources, "*.mm")
|
||||
mod_lib = env_modules.add_library("#bin/libgodot_icloud_module" + env["LIBSUFFIX"], modules_sources)
|
||||
6
modules/icloud/config.py
Normal file
6
modules/icloud/config.py
Normal file
@@ -0,0 +1,6 @@
|
||||
def can_build(env, platform):
|
||||
return platform == "iphone"
|
||||
|
||||
|
||||
def configure(env):
|
||||
pass
|
||||
17
modules/icloud/icloud.gdip
Normal file
17
modules/icloud/icloud.gdip
Normal file
@@ -0,0 +1,17 @@
|
||||
[config]
|
||||
name="iCloud"
|
||||
binary="icloud_lib.a"
|
||||
|
||||
initialization="register_icloud_types"
|
||||
deinitialization="unregister_icloud_types"
|
||||
|
||||
[dependencies]
|
||||
linked=[]
|
||||
embedded=[]
|
||||
system=[]
|
||||
|
||||
capabilities=[]
|
||||
|
||||
files=[]
|
||||
|
||||
[plist]
|
||||
61
modules/icloud/icloud.h
Normal file
61
modules/icloud/icloud.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*************************************************************************/
|
||||
/* icloud.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef ICLOUD_H
|
||||
#define ICLOUD_H
|
||||
|
||||
#include "core/object.h"
|
||||
|
||||
class ICloud : public Object {
|
||||
|
||||
GDCLASS(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
|
||||
347
modules/icloud/icloud.mm
Normal file
347
modules/icloud/icloud.mm
Normal file
@@ -0,0 +1,347 @@
|
||||
/*************************************************************************/
|
||||
/* icloud.mm */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "icloud.h"
|
||||
|
||||
#import "platform/iphone/app_delegate.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
ICloud *ICloud::instance = NULL;
|
||||
|
||||
void ICloud::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("remove_key"), &ICloud::remove_key);
|
||||
ClassDB::bind_method(D_METHOD("set_key_values"), &ICloud::set_key_values);
|
||||
ClassDB::bind_method(D_METHOD("get_key_value"), &ICloud::get_key_value);
|
||||
ClassDB::bind_method(D_METHOD("synchronize_key_values"), &ICloud::synchronize_key_values);
|
||||
ClassDB::bind_method(D_METHOD("get_all_key_values"), &ICloud::get_all_key_values);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_pending_event_count"), &ICloud::get_pending_event_count);
|
||||
ClassDB::bind_method(D_METHOD("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]]) {
|
||||
PoolByteArray ret;
|
||||
NSData *data = (NSData *)object;
|
||||
if ([data length] > 0) {
|
||||
ret.resize([data length]);
|
||||
{
|
||||
PoolByteArray::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 {
|
||||
return Variant();
|
||||
}
|
||||
} 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()];
|
||||
} 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];
|
||||
Dictionary dic = v;
|
||||
Array keys = dic.keys();
|
||||
for (int i = 0; i < keys.size(); ++i) {
|
||||
NSString *key = [[NSString alloc] initWithUTF8String:((String)(keys[i])).utf8().get_data()];
|
||||
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];
|
||||
Array arr = v;
|
||||
for (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::POOL_BYTE_ARRAY) {
|
||||
PoolByteArray arr = v;
|
||||
PoolByteArray::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()];
|
||||
|
||||
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 (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()];
|
||||
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()];
|
||||
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: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";
|
||||
|
||||
//PoolStringArray 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() {}
|
||||
48
modules/icloud/icloud_module.cpp
Normal file
48
modules/icloud/icloud_module.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/*************************************************************************/
|
||||
/* icloud_module.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "icloud_module.h"
|
||||
|
||||
#include "core/engine.h"
|
||||
|
||||
#include "icloud.h"
|
||||
|
||||
ICloud *icloud;
|
||||
|
||||
void register_icloud_types() {
|
||||
icloud = memnew(ICloud);
|
||||
Engine::get_singleton()->add_singleton(Engine::Singleton("ICloud", icloud));
|
||||
}
|
||||
|
||||
void unregister_icloud_types() {
|
||||
if (icloud) {
|
||||
memdelete(icloud);
|
||||
}
|
||||
}
|
||||
32
modules/icloud/icloud_module.h
Normal file
32
modules/icloud/icloud_module.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*************************************************************************/
|
||||
/* icloud_module.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
void register_icloud_types();
|
||||
void unregister_icloud_types();
|
||||
15
modules/inappstore/SCsub
Normal file
15
modules/inappstore/SCsub
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
Import("env_modules")
|
||||
|
||||
env_inappstore = env_modules.Clone()
|
||||
|
||||
# (iOS) Enable module support
|
||||
env_inappstore.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
|
||||
|
||||
# (iOS) Build as separate static library
|
||||
modules_sources = []
|
||||
env_inappstore.add_source_files(modules_sources, "*.cpp")
|
||||
env_inappstore.add_source_files(modules_sources, "*.mm")
|
||||
mod_lib = env_modules.add_library("#bin/libgodot_inappstore_module" + env["LIBSUFFIX"], modules_sources)
|
||||
6
modules/inappstore/config.py
Normal file
6
modules/inappstore/config.py
Normal file
@@ -0,0 +1,6 @@
|
||||
def can_build(env, platform):
|
||||
return platform == "iphone"
|
||||
|
||||
|
||||
def configure(env):
|
||||
pass
|
||||
78
modules/inappstore/in_app_store.h
Normal file
78
modules/inappstore/in_app_store.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*************************************************************************/
|
||||
/* in_app_store.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef IN_APP_STORE_H
|
||||
#define IN_APP_STORE_H
|
||||
|
||||
#include "core/object.h"
|
||||
|
||||
#ifdef __OBJC__
|
||||
@class GodotProductsDelegate;
|
||||
@class GodotTransactionsObserver;
|
||||
|
||||
typedef GodotProductsDelegate InAppStoreProductDelegate;
|
||||
typedef GodotTransactionsObserver InAppStoreTransactionObserver;
|
||||
#else
|
||||
typedef void InAppStoreProductDelegate;
|
||||
typedef void InAppStoreTransactionObserver;
|
||||
#endif
|
||||
|
||||
class InAppStore : public Object {
|
||||
|
||||
GDCLASS(InAppStore, Object);
|
||||
|
||||
static InAppStore *instance;
|
||||
static void _bind_methods();
|
||||
|
||||
List<Variant> pending_events;
|
||||
|
||||
InAppStoreProductDelegate *products_request_delegate;
|
||||
InAppStoreTransactionObserver *transactions_observer;
|
||||
|
||||
public:
|
||||
Error request_product_info(Variant p_params);
|
||||
Error restore_purchases();
|
||||
Error purchase(Variant p_params);
|
||||
|
||||
int get_pending_event_count();
|
||||
Variant pop_pending_event();
|
||||
void finish_transaction(String product_id);
|
||||
void set_auto_finish_transaction(bool b);
|
||||
|
||||
void _post_event(Variant p_event);
|
||||
void _record_purchase(String product_id);
|
||||
|
||||
static InAppStore *get_singleton();
|
||||
|
||||
InAppStore();
|
||||
~InAppStore();
|
||||
};
|
||||
|
||||
#endif
|
||||
414
modules/inappstore/in_app_store.mm
Normal file
414
modules/inappstore/in_app_store.mm
Normal file
@@ -0,0 +1,414 @@
|
||||
/*************************************************************************/
|
||||
/* in_app_store.mm */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "in_app_store.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <StoreKit/StoreKit.h>
|
||||
|
||||
InAppStore *InAppStore::instance = NULL;
|
||||
|
||||
@interface SKProduct (LocalizedPrice)
|
||||
|
||||
@property(nonatomic, readonly) NSString *localizedPrice;
|
||||
|
||||
@end
|
||||
|
||||
//----------------------------------//
|
||||
// SKProduct extension
|
||||
//----------------------------------//
|
||||
@implementation SKProduct (LocalizedPrice)
|
||||
|
||||
- (NSString *)localizedPrice {
|
||||
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
|
||||
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
|
||||
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
|
||||
[numberFormatter setLocale:self.priceLocale];
|
||||
NSString *formattedString = [numberFormatter stringFromNumber:self.price];
|
||||
return formattedString;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface GodotProductsDelegate : NSObject <SKProductsRequestDelegate>
|
||||
|
||||
@property(nonatomic, strong) NSMutableArray *loadedProducts;
|
||||
@property(nonatomic, strong) NSMutableArray *pendingRequests;
|
||||
|
||||
- (void)performRequestWithProductIDs:(NSSet *)productIDs;
|
||||
- (Error)purchaseProductWithProductID:(NSString *)productID;
|
||||
- (void)reset;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GodotProductsDelegate
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
[self godot_commonInit];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)godot_commonInit {
|
||||
self.loadedProducts = [NSMutableArray new];
|
||||
self.pendingRequests = [NSMutableArray new];
|
||||
}
|
||||
|
||||
- (void)performRequestWithProductIDs:(NSSet *)productIDs {
|
||||
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:productIDs];
|
||||
|
||||
request.delegate = self;
|
||||
[self.pendingRequests addObject:request];
|
||||
[request start];
|
||||
}
|
||||
|
||||
- (Error)purchaseProductWithProductID:(NSString *)productID {
|
||||
SKProduct *product = nil;
|
||||
|
||||
NSLog(@"searching for product!");
|
||||
|
||||
if (self.loadedProducts) {
|
||||
for (SKProduct *storedProduct in self.loadedProducts) {
|
||||
if ([storedProduct.productIdentifier isEqualToString:productID]) {
|
||||
product = storedProduct;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!product) {
|
||||
return ERR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
NSLog(@"product found!");
|
||||
|
||||
SKPayment *payment = [SKPayment paymentWithProduct:product];
|
||||
[[SKPaymentQueue defaultQueue] addPayment:payment];
|
||||
|
||||
NSLog(@"purchase sent!");
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
- (void)reset {
|
||||
[self.loadedProducts removeAllObjects];
|
||||
[self.pendingRequests removeAllObjects];
|
||||
}
|
||||
|
||||
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
|
||||
[self.pendingRequests removeObject:request];
|
||||
|
||||
Dictionary ret;
|
||||
ret["type"] = "product_info";
|
||||
ret["result"] = "error";
|
||||
ret["error"] = String::utf8([error.localizedDescription UTF8String]);
|
||||
|
||||
InAppStore::get_singleton()->_post_event(ret);
|
||||
}
|
||||
|
||||
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
|
||||
[self.pendingRequests removeObject:request];
|
||||
|
||||
NSArray *products = response.products;
|
||||
[self.loadedProducts addObjectsFromArray:products];
|
||||
|
||||
Dictionary ret;
|
||||
ret["type"] = "product_info";
|
||||
ret["result"] = "ok";
|
||||
PoolStringArray titles;
|
||||
PoolStringArray descriptions;
|
||||
PoolRealArray prices;
|
||||
PoolStringArray ids;
|
||||
PoolStringArray localized_prices;
|
||||
PoolStringArray currency_codes;
|
||||
|
||||
for (NSUInteger i = 0; i < [products count]; i++) {
|
||||
SKProduct *product = [products objectAtIndex:i];
|
||||
|
||||
const char *str = [product.localizedTitle UTF8String];
|
||||
titles.push_back(String::utf8(str != NULL ? str : ""));
|
||||
|
||||
str = [product.localizedDescription UTF8String];
|
||||
descriptions.push_back(String::utf8(str != NULL ? str : ""));
|
||||
prices.push_back([product.price doubleValue]);
|
||||
ids.push_back(String::utf8([product.productIdentifier UTF8String]));
|
||||
localized_prices.push_back(String::utf8([product.localizedPrice UTF8String]));
|
||||
currency_codes.push_back(String::utf8([[[product priceLocale] objectForKey:NSLocaleCurrencyCode] UTF8String]));
|
||||
}
|
||||
|
||||
ret["titles"] = titles;
|
||||
ret["descriptions"] = descriptions;
|
||||
ret["prices"] = prices;
|
||||
ret["ids"] = ids;
|
||||
ret["localized_prices"] = localized_prices;
|
||||
ret["currency_codes"] = currency_codes;
|
||||
|
||||
PoolStringArray invalid_ids;
|
||||
|
||||
for (NSString *ipid in response.invalidProductIdentifiers) {
|
||||
invalid_ids.push_back(String::utf8([ipid UTF8String]));
|
||||
}
|
||||
|
||||
ret["invalid_ids"] = invalid_ids;
|
||||
|
||||
InAppStore::get_singleton()->_post_event(ret);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface GodotTransactionsObserver : NSObject <SKPaymentTransactionObserver>
|
||||
|
||||
@property(nonatomic, assign) BOOL shouldAutoFinishTransactions;
|
||||
@property(nonatomic, strong) NSMutableDictionary *pendingTransactions;
|
||||
|
||||
- (void)finishTransactionWithProductID:(NSString *)productID;
|
||||
- (void)reset;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GodotTransactionsObserver
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
[self godot_commonInit];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)godot_commonInit {
|
||||
self.pendingTransactions = [NSMutableDictionary new];
|
||||
}
|
||||
|
||||
- (void)finishTransactionWithProductID:(NSString *)productID {
|
||||
SKPaymentTransaction *transaction = self.pendingTransactions[productID];
|
||||
|
||||
if (transaction) {
|
||||
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
|
||||
}
|
||||
|
||||
self.pendingTransactions[productID] = nil;
|
||||
}
|
||||
|
||||
- (void)reset {
|
||||
[self.pendingTransactions removeAllObjects];
|
||||
}
|
||||
|
||||
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
|
||||
|
||||
printf("transactions updated!\n");
|
||||
for (SKPaymentTransaction *transaction in transactions) {
|
||||
|
||||
switch (transaction.transactionState) {
|
||||
case SKPaymentTransactionStatePurchased: {
|
||||
printf("status purchased!\n");
|
||||
String pid = String::utf8([transaction.payment.productIdentifier UTF8String]);
|
||||
String transactionId = String::utf8([transaction.transactionIdentifier UTF8String]);
|
||||
InAppStore::get_singleton()->_record_purchase(pid);
|
||||
Dictionary ret;
|
||||
ret["type"] = "purchase";
|
||||
ret["result"] = "ok";
|
||||
ret["product_id"] = pid;
|
||||
ret["transaction_id"] = transactionId;
|
||||
|
||||
NSData *receipt = nil;
|
||||
int sdk_version = [[[UIDevice currentDevice] systemVersion] intValue];
|
||||
|
||||
NSBundle *bundle = [NSBundle mainBundle];
|
||||
// Get the transaction receipt file path location in the app bundle.
|
||||
NSURL *receiptFileURL = [bundle appStoreReceiptURL];
|
||||
|
||||
// Read in the contents of the transaction file.
|
||||
receipt = [NSData dataWithContentsOfURL:receiptFileURL];
|
||||
|
||||
NSString *receipt_to_send = nil;
|
||||
|
||||
if (receipt != nil) {
|
||||
receipt_to_send = [receipt base64EncodedStringWithOptions:0];
|
||||
}
|
||||
Dictionary receipt_ret;
|
||||
receipt_ret["receipt"] = String::utf8(receipt_to_send != nil ? [receipt_to_send UTF8String] : "");
|
||||
receipt_ret["sdk"] = sdk_version;
|
||||
ret["receipt"] = receipt_ret;
|
||||
|
||||
InAppStore::get_singleton()->_post_event(ret);
|
||||
|
||||
if (self.shouldAutoFinishTransactions) {
|
||||
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
|
||||
} else {
|
||||
self.pendingTransactions[transaction.payment.productIdentifier] = transaction;
|
||||
}
|
||||
|
||||
} break;
|
||||
case SKPaymentTransactionStateFailed: {
|
||||
printf("status transaction failed!\n");
|
||||
String pid = String::utf8([transaction.payment.productIdentifier UTF8String]);
|
||||
Dictionary ret;
|
||||
ret["type"] = "purchase";
|
||||
ret["result"] = "error";
|
||||
ret["product_id"] = pid;
|
||||
ret["error"] = String::utf8([transaction.error.localizedDescription UTF8String]);
|
||||
InAppStore::get_singleton()->_post_event(ret);
|
||||
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
|
||||
} break;
|
||||
case SKPaymentTransactionStateRestored: {
|
||||
printf("status transaction restored!\n");
|
||||
String pid = String::utf8([transaction.originalTransaction.payment.productIdentifier UTF8String]);
|
||||
InAppStore::get_singleton()->_record_purchase(pid);
|
||||
Dictionary ret;
|
||||
ret["type"] = "restore";
|
||||
ret["result"] = "ok";
|
||||
ret["product_id"] = pid;
|
||||
InAppStore::get_singleton()->_post_event(ret);
|
||||
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
|
||||
} break;
|
||||
default: {
|
||||
printf("status default %i!\n", (int)transaction.transactionState);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
void InAppStore::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("request_product_info"), &InAppStore::request_product_info);
|
||||
ClassDB::bind_method(D_METHOD("restore_purchases"), &InAppStore::restore_purchases);
|
||||
ClassDB::bind_method(D_METHOD("purchase"), &InAppStore::purchase);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_pending_event_count"), &InAppStore::get_pending_event_count);
|
||||
ClassDB::bind_method(D_METHOD("pop_pending_event"), &InAppStore::pop_pending_event);
|
||||
ClassDB::bind_method(D_METHOD("finish_transaction"), &InAppStore::finish_transaction);
|
||||
ClassDB::bind_method(D_METHOD("set_auto_finish_transaction"), &InAppStore::set_auto_finish_transaction);
|
||||
}
|
||||
|
||||
Error InAppStore::request_product_info(Variant p_params) {
|
||||
Dictionary params = p_params;
|
||||
ERR_FAIL_COND_V(!params.has("product_ids"), ERR_INVALID_PARAMETER);
|
||||
|
||||
PoolStringArray pids = params["product_ids"];
|
||||
printf("************ request product info! %i\n", pids.size());
|
||||
|
||||
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:pids.size()];
|
||||
for (int i = 0; i < pids.size(); i++) {
|
||||
printf("******** adding %ls to product list\n", pids[i].c_str());
|
||||
NSString *pid = [[NSString alloc] initWithUTF8String:pids[i].utf8().get_data()];
|
||||
[array addObject:pid];
|
||||
}
|
||||
|
||||
NSSet *products = [[NSSet alloc] initWithArray:array];
|
||||
|
||||
[products_request_delegate performRequestWithProductIDs:products];
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error InAppStore::restore_purchases() {
|
||||
printf("restoring purchases!\n");
|
||||
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error InAppStore::purchase(Variant p_params) {
|
||||
ERR_FAIL_COND_V(![SKPaymentQueue canMakePayments], ERR_UNAVAILABLE);
|
||||
if (![SKPaymentQueue canMakePayments]) {
|
||||
return ERR_UNAVAILABLE;
|
||||
}
|
||||
|
||||
printf("purchasing!\n");
|
||||
Dictionary params = p_params;
|
||||
ERR_FAIL_COND_V(!params.has("product_id"), ERR_INVALID_PARAMETER);
|
||||
|
||||
NSString *pid = [[NSString alloc] initWithUTF8String:String(params["product_id"]).utf8().get_data()];
|
||||
|
||||
return [products_request_delegate purchaseProductWithProductID:pid];
|
||||
}
|
||||
|
||||
int InAppStore::get_pending_event_count() {
|
||||
return pending_events.size();
|
||||
}
|
||||
|
||||
Variant InAppStore::pop_pending_event() {
|
||||
Variant front = pending_events.front()->get();
|
||||
pending_events.pop_front();
|
||||
|
||||
return front;
|
||||
}
|
||||
|
||||
void InAppStore::_post_event(Variant p_event) {
|
||||
pending_events.push_back(p_event);
|
||||
}
|
||||
|
||||
void InAppStore::_record_purchase(String product_id) {
|
||||
String skey = "purchased/" + product_id;
|
||||
NSString *key = [[NSString alloc] initWithUTF8String:skey.utf8().get_data()];
|
||||
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:key];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
}
|
||||
|
||||
InAppStore *InAppStore::get_singleton() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
InAppStore::InAppStore() {
|
||||
ERR_FAIL_COND(instance != NULL);
|
||||
instance = this;
|
||||
|
||||
products_request_delegate = [[GodotProductsDelegate alloc] init];
|
||||
transactions_observer = [[GodotTransactionsObserver alloc] init];
|
||||
|
||||
[[SKPaymentQueue defaultQueue] addTransactionObserver:transactions_observer];
|
||||
}
|
||||
|
||||
void InAppStore::finish_transaction(String product_id) {
|
||||
NSString *prod_id = [NSString stringWithCString:product_id.utf8().get_data() encoding:NSUTF8StringEncoding];
|
||||
|
||||
[transactions_observer finishTransactionWithProductID:prod_id];
|
||||
}
|
||||
|
||||
void InAppStore::set_auto_finish_transaction(bool b) {
|
||||
transactions_observer.shouldAutoFinishTransactions = b;
|
||||
}
|
||||
|
||||
InAppStore::~InAppStore() {
|
||||
[products_request_delegate reset];
|
||||
[transactions_observer reset];
|
||||
|
||||
products_request_delegate = nil;
|
||||
[[SKPaymentQueue defaultQueue] removeTransactionObserver:transactions_observer];
|
||||
transactions_observer = nil;
|
||||
}
|
||||
48
modules/inappstore/in_app_store_module.cpp
Normal file
48
modules/inappstore/in_app_store_module.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/*************************************************************************/
|
||||
/* in_app_store_module.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "in_app_store_module.h"
|
||||
|
||||
#include "core/engine.h"
|
||||
|
||||
#include "in_app_store.h"
|
||||
|
||||
InAppStore *store_kit;
|
||||
|
||||
void register_inappstore_types() {
|
||||
store_kit = memnew(InAppStore);
|
||||
Engine::get_singleton()->add_singleton(Engine::Singleton("InAppStore", store_kit));
|
||||
}
|
||||
|
||||
void unregister_inappstore_types() {
|
||||
if (store_kit) {
|
||||
memdelete(store_kit);
|
||||
}
|
||||
}
|
||||
32
modules/inappstore/in_app_store_module.h
Normal file
32
modules/inappstore/in_app_store_module.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*************************************************************************/
|
||||
/* in_app_store_module.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
void register_inappstore_types();
|
||||
void unregister_inappstore_types();
|
||||
17
modules/inappstore/inappstore.gdip
Normal file
17
modules/inappstore/inappstore.gdip
Normal file
@@ -0,0 +1,17 @@
|
||||
[config]
|
||||
name="InAppStore"
|
||||
binary="inappstore_lib.a"
|
||||
|
||||
initialization="register_inappstore_types"
|
||||
deinitialization="unregister_inappstore_types"
|
||||
|
||||
[dependencies]
|
||||
linked=[]
|
||||
embedded=[]
|
||||
system=["StoreKit.framework"]
|
||||
|
||||
capabilities=[]
|
||||
|
||||
files=[]
|
||||
|
||||
[plist]
|
||||
Reference in New Issue
Block a user