diff options
Diffstat (limited to 'platform')
-rw-r--r-- | platform/android/export/export.cpp | 2 | ||||
-rw-r--r-- | platform/iphone/game_center.h | 2 | ||||
-rw-r--r-- | platform/iphone/icloud.h | 2 | ||||
-rw-r--r-- | platform/iphone/in_app_store.h | 16 | ||||
-rw-r--r-- | platform/iphone/in_app_store.mm | 284 | ||||
-rw-r--r-- | platform/iphone/ios.h | 2 | ||||
-rw-r--r-- | platform/javascript/SCsub | 3 | ||||
-rw-r--r-- | platform/javascript/api/api.cpp | 2 | ||||
-rw-r--r-- | platform/javascript/api/javascript_eval.h | 2 | ||||
-rw-r--r-- | platform/javascript/api/javascript_tools_editor_plugin.cpp | 153 | ||||
-rw-r--r-- | platform/javascript/api/javascript_tools_editor_plugin.h | 62 | ||||
-rw-r--r-- | platform/javascript/detect.py | 2 | ||||
-rw-r--r-- | platform/javascript/engine/engine.js | 8 | ||||
-rw-r--r-- | platform/javascript/native/utils.js | 21 | ||||
-rw-r--r-- | platform/osx/detect.py | 3 | ||||
-rw-r--r-- | platform/osx/display_server_osx.mm | 14 | ||||
-rw-r--r-- | platform/uwp/detect.py | 2 | ||||
-rw-r--r-- | platform/uwp/export/export.cpp | 3 | ||||
-rw-r--r-- | platform/windows/detect.py | 2 |
19 files changed, 473 insertions, 112 deletions
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 5e6cc3e4e2..37da1dbd4d 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -592,7 +592,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { zipfi.tmz_date.tm_hour = time.hour; zipfi.tmz_date.tm_mday = date.day; zipfi.tmz_date.tm_min = time.min; - zipfi.tmz_date.tm_mon = date.month; + zipfi.tmz_date.tm_mon = date.month - 1; // tm_mon is zero indexed zipfi.tmz_date.tm_sec = time.sec; zipfi.tmz_date.tm_year = date.year; zipfi.dosDate = 0; diff --git a/platform/iphone/game_center.h b/platform/iphone/game_center.h index 6705674ac6..10c8ef52fb 100644 --- a/platform/iphone/game_center.h +++ b/platform/iphone/game_center.h @@ -33,7 +33,7 @@ #ifndef GAME_CENTER_H #define GAME_CENTER_H -#include "core/object.h" +#include "core/class_db.h" class GameCenter : public Object { GDCLASS(GameCenter, Object); diff --git a/platform/iphone/icloud.h b/platform/iphone/icloud.h index 381edfa718..6ca1e6594a 100644 --- a/platform/iphone/icloud.h +++ b/platform/iphone/icloud.h @@ -33,7 +33,7 @@ #ifndef ICLOUD_H #define ICLOUD_H -#include "core/object.h" +#include "core/class_db.h" class ICloud : public Object { GDCLASS(ICloud, Object); diff --git a/platform/iphone/in_app_store.h b/platform/iphone/in_app_store.h index beb58af2c7..1b8f84ec7b 100644 --- a/platform/iphone/in_app_store.h +++ b/platform/iphone/in_app_store.h @@ -33,7 +33,18 @@ #ifndef IN_APP_STORE_H #define IN_APP_STORE_H -#include "core/object.h" +#include "core/class_db.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); @@ -43,6 +54,9 @@ class InAppStore : public Object { List<Variant> pending_events; + InAppStoreProductDelegate *products_request_delegate; + InAppStoreTransactionObserver *transactions_observer; + public: Error request_product_info(Dictionary p_params); Error restore_purchases(); diff --git a/platform/iphone/in_app_store.mm b/platform/iphone/in_app_store.mm index 2b973dfbcf..3eec9dae69 100644 --- a/platform/iphone/in_app_store.mm +++ b/platform/iphone/in_app_store.mm @@ -32,24 +32,22 @@ #include "in_app_store.h" -extern "C" { #import <Foundation/Foundation.h> #import <StoreKit/StoreKit.h> -}; -bool auto_finish_transactions = true; -NSMutableDictionary *pending_transactions = [NSMutableDictionary dictionary]; -static NSArray *latestProducts; +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]; @@ -61,29 +59,91 @@ static NSArray *latestProducts; @end -InAppStore *InAppStore::instance = NULL; - -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); +@interface GodotProductsDelegate : NSObject <SKProductsRequestDelegate> - 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); -}; +@property(nonatomic, strong) NSMutableArray *loadedProducts; +@property(nonatomic, strong) NSMutableArray *pendingRequests; -@interface ProductsDelegate : NSObject <SKProductsRequestDelegate> { -}; +- (void)performRequestWithProductIDs:(NSSet *)productIDs; +- (Error)purchaseProductWithProductID:(NSString *)productID; +- (void)reset; @end -@implementation ProductsDelegate +@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; - latestProducts = products; + [self.loadedProducts addObjectsFromArray:products]; Dictionary ret; ret["type"] = "product_info"; @@ -107,7 +167,8 @@ void InAppStore::_bind_methods() { 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; @@ -119,50 +180,54 @@ void InAppStore::_bind_methods() { 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 -Error InAppStore::request_product_info(Dictionary p_params) { - ERR_FAIL_COND_V(!p_params.has("product_ids"), ERR_INVALID_PARAMETER); +@interface GodotTransactionsObserver : NSObject <SKPaymentTransactionObserver> - PackedStringArray pids = p_params["product_ids"]; - printf("************ request product info! %i\n", pids.size()); +@property(nonatomic, assign) BOOL shouldAutoFinishTransactions; +@property(nonatomic, strong) NSMutableDictionary *pendingTransactions; - NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:pids.size()]; - for (int i = 0; i < pids.size(); i++) { - printf("******** adding %s to product list\n", pids[i].utf8().get_data()); - NSString *pid = [[NSString alloc] initWithUTF8String:pids[i].utf8().get_data()]; - [array addObject:pid]; - }; +- (void)finishTransactionWithProductID:(NSString *)productID; +- (void)reset; - NSSet *products = [[NSSet alloc] initWithArray:array]; - SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:products]; +@end - ProductsDelegate *delegate = [[ProductsDelegate alloc] init]; +@implementation GodotTransactionsObserver - request.delegate = delegate; - [request start]; +- (instancetype)init { + self = [super init]; - return OK; -}; + if (self) { + [self godot_commonInit]; + } -Error InAppStore::restore_purchases() { - printf("restoring purchases!\n"); - [[SKPaymentQueue defaultQueue] restoreCompletedTransactions]; + return self; +} - return OK; -}; +- (void)godot_commonInit { + self.pendingTransactions = [NSMutableDictionary new]; +} -@interface TransObserver : NSObject <SKPaymentTransactionObserver> { -}; -@end +- (void)finishTransactionWithProductID:(NSString *)productID { + SKPaymentTransaction *transaction = self.pendingTransactions[productID]; + + if (transaction) { + [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; + } -@implementation TransObserver + self.pendingTransactions[productID] = nil; +} + +- (void)reset { + [self.pendingTransactions removeAllObjects]; +} - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { printf("transactions updated!\n"); @@ -190,6 +255,7 @@ Error InAppStore::restore_purchases() { receipt = [NSData dataWithContentsOfURL:receiptFileURL]; NSString *receipt_to_send = nil; + if (receipt != nil) { receipt_to_send = [receipt base64EncodedStringWithOptions:0]; } @@ -200,13 +266,13 @@ Error InAppStore::restore_purchases() { InAppStore::get_singleton()->_post_event(ret); - if (auto_finish_transactions) { + if (self.shouldAutoFinishTransactions) { [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; } else { - [pending_transactions setObject:transaction forKey:transaction.payment.productIdentifier]; + self.pendingTransactions[transaction.payment.productIdentifier] = transaction; } - }; break; + } break; case SKPaymentTransactionStateFailed: { printf("status transaction failed!\n"); String pid = String::utf8([transaction.payment.productIdentifier UTF8String]); @@ -231,95 +297,119 @@ Error InAppStore::restore_purchases() { } break; default: { printf("status default %i!\n", (int)transaction.transactionState); - }; break; - }; - }; -}; + } break; + } + } +} @end -Error InAppStore::purchase(Dictionary p_params) { - ERR_FAIL_COND_V(![SKPaymentQueue canMakePayments], ERR_UNAVAILABLE); - if (![SKPaymentQueue canMakePayments]) - return ERR_UNAVAILABLE; +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); - printf("purchasing!\n"); - ERR_FAIL_COND_V(!p_params.has("product_id"), ERR_INVALID_PARAMETER); + 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); +} - NSString *pid = [[NSString alloc] initWithUTF8String:String(p_params["product_id"]).utf8().get_data()]; +Error InAppStore::request_product_info(Dictionary p_params) { + ERR_FAIL_COND_V(!p_params.has("product_ids"), ERR_INVALID_PARAMETER); - SKProduct *product = nil; + PackedStringArray pids = p_params["product_ids"]; + printf("************ request product info! %i\n", pids.size()); - if (latestProducts) { - for (SKProduct *storedProduct in latestProducts) { - if ([storedProduct.productIdentifier isEqualToString:pid]) { - product = storedProduct; - break; - } - } - } + NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:pids.size()]; + for (int i = 0; i < pids.size(); i++) { + printf("******** adding %s to product list\n", pids[i].utf8().get_data()); + NSString *pid = [[NSString alloc] initWithUTF8String:pids[i].utf8().get_data()]; + [array addObject:pid]; + }; - if (!product) { - return ERR_INVALID_PARAMETER; - } + NSSet *products = [[NSSet alloc] initWithArray:array]; - SKPayment *payment = [SKPayment paymentWithProduct:product]; - SKPaymentQueue *defq = [SKPaymentQueue defaultQueue]; - [defq addPayment:payment]; - printf("purchase sent!\n"); + [products_request_delegate performRequestWithProductIDs:products]; return OK; -}; +} + +Error InAppStore::restore_purchases() { + printf("restoring purchases!\n"); + [[SKPaymentQueue defaultQueue] restoreCompletedTransactions]; + + return OK; +} + +Error InAppStore::purchase(Dictionary 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; - auto_finish_transactions = false; - TransObserver *observer = [[TransObserver alloc] init]; - [[SKPaymentQueue defaultQueue] addTransactionObserver:observer]; - //pending_transactions = [NSMutableDictionary dictionary]; -}; + 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]; - if ([pending_transactions objectForKey:prod_id]) { - [[SKPaymentQueue defaultQueue] finishTransaction:[pending_transactions objectForKey:prod_id]]; - [pending_transactions removeObjectForKey:prod_id]; - } -}; + [transactions_observer finishTransactionWithProductID:prod_id]; +} void InAppStore::set_auto_finish_transaction(bool b) { - auto_finish_transactions = b; + transactions_observer.shouldAutoFinishTransactions = b; } -InAppStore::~InAppStore() {} +InAppStore::~InAppStore() { + [products_request_delegate reset]; + [transactions_observer reset]; + + products_request_delegate = nil; + [[SKPaymentQueue defaultQueue] removeTransactionObserver:transactions_observer]; + transactions_observer = nil; +} #endif diff --git a/platform/iphone/ios.h b/platform/iphone/ios.h index 2b29e6f268..6a89cd38cb 100644 --- a/platform/iphone/ios.h +++ b/platform/iphone/ios.h @@ -31,7 +31,7 @@ #ifndef IOS_H #define IOS_H -#include "core/object.h" +#include "core/class_db.h" class iOS : public Object { GDCLASS(iOS, Object); diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub index a8861124b9..7381ea13b7 100644 --- a/platform/javascript/SCsub +++ b/platform/javascript/SCsub @@ -9,6 +9,7 @@ javascript_files = [ "javascript_eval.cpp", "javascript_main.cpp", "os_javascript.cpp", + "api/javascript_tools_editor_plugin.cpp", ] build_targets = ["#bin/godot${PROGSUFFIX}.js", "#bin/godot${PROGSUFFIX}.wasm"] @@ -55,7 +56,7 @@ out_files = [ zip_dir.File(binary_name + ".wasm"), zip_dir.File(binary_name + ".html"), ] -html_file = "#misc/dist/html/full-size.html" +html_file = "#misc/dist/html/editor.html" if env["tools"] else "#misc/dist/html/full-size.html" in_files = [js_wrapped, build[1], html_file] if env["threads_enabled"]: in_files.append(build[2]) diff --git a/platform/javascript/api/api.cpp b/platform/javascript/api/api.cpp index 9c73e5c4c4..aa0206d144 100644 --- a/platform/javascript/api/api.cpp +++ b/platform/javascript/api/api.cpp @@ -31,10 +31,12 @@ #include "api.h" #include "core/engine.h" #include "javascript_eval.h" +#include "javascript_tools_editor_plugin.h" static JavaScript *javascript_eval; void register_javascript_api() { + JavaScriptToolsEditorPlugin::initialize(); ClassDB::register_virtual_class<JavaScript>(); javascript_eval = memnew(JavaScript); Engine::get_singleton()->add_singleton(Engine::Singleton("JavaScript", javascript_eval)); diff --git a/platform/javascript/api/javascript_eval.h b/platform/javascript/api/javascript_eval.h index 29229de8e3..26b5b9e484 100644 --- a/platform/javascript/api/javascript_eval.h +++ b/platform/javascript/api/javascript_eval.h @@ -31,7 +31,7 @@ #ifndef JAVASCRIPT_EVAL_H #define JAVASCRIPT_EVAL_H -#include "core/object.h" +#include "core/class_db.h" class JavaScript : public Object { private: diff --git a/platform/javascript/api/javascript_tools_editor_plugin.cpp b/platform/javascript/api/javascript_tools_editor_plugin.cpp new file mode 100644 index 0000000000..e487bf23b7 --- /dev/null +++ b/platform/javascript/api/javascript_tools_editor_plugin.cpp @@ -0,0 +1,153 @@ +/*************************************************************************/ +/* javascript_tools_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 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. */ +/*************************************************************************/ + +#if defined(TOOLS_ENABLED) && defined(JAVASCRIPT_ENABLED) +#include "javascript_tools_editor_plugin.h" + +#include "core/engine.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" +#include "core/project_settings.h" +#include "editor/editor_node.h" + +#include <emscripten/emscripten.h> + +static void _javascript_editor_init_callback() { + EditorNode::get_singleton()->add_editor_plugin(memnew(JavaScriptToolsEditorPlugin(EditorNode::get_singleton()))); +} + +void JavaScriptToolsEditorPlugin::initialize() { + EditorNode::add_init_callback(_javascript_editor_init_callback); +} + +JavaScriptToolsEditorPlugin::JavaScriptToolsEditorPlugin(EditorNode *p_editor) { + Variant v; + add_tool_menu_item("Download Project Source", this, "_download_zip", v); +} + +void JavaScriptToolsEditorPlugin::_download_zip(Variant p_v) { + if (!Engine::get_singleton() || !Engine::get_singleton()->is_editor_hint()) { + WARN_PRINT("Project download is only available in Editor mode"); + return; + } + String resource_path = ProjectSettings::get_singleton()->get_resource_path(); + + FileAccess *src_f; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + zipFile zip = zipOpen2("/tmp/project.zip", APPEND_STATUS_CREATE, NULL, &io); + String base_path = resource_path.substr(0, resource_path.rfind("/")) + "/"; + _zip_recursive(resource_path, base_path, zip); + zipClose(zip, NULL); + EM_ASM({ + const path = "/tmp/project.zip"; + const size = FS.stat(path)["size"]; + const buf = new Uint8Array(size); + const fd = FS.open(path, "r"); + FS.read(fd, buf, 0, size); + FS.close(fd); + FS.unlink(path); + const blob = new Blob([buf], { type: "application/zip" }); + const url = window.URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = "project.zip"; + a.style.display = "none"; + document.body.appendChild(a); + a.click(); + a.remove(); + window.URL.revokeObjectURL(url); + }); +} + +void JavaScriptToolsEditorPlugin::_bind_methods() { + ClassDB::bind_method("_download_zip", &JavaScriptToolsEditorPlugin::_download_zip); +} + +void JavaScriptToolsEditorPlugin::_zip_file(String p_path, String p_base_path, zipFile p_zip) { + FileAccess *f = FileAccess::open(p_path, FileAccess::READ); + if (!f) { + WARN_PRINT("Unable to open file for zipping: " + p_path); + return; + } + Vector<uint8_t> data; + int len = f->get_len(); + data.resize(len); + f->get_buffer(data.ptrw(), len); + f->close(); + memdelete(f); + + String path = p_path.replace_first(p_base_path, ""); + zipOpenNewFileInZip(p_zip, + path.utf8().get_data(), + NULL, + NULL, + 0, + NULL, + 0, + NULL, + Z_DEFLATED, + Z_DEFAULT_COMPRESSION); + zipWriteInFileInZip(p_zip, data.ptr(), data.size()); + zipCloseFileInZip(p_zip); +} + +void JavaScriptToolsEditorPlugin::_zip_recursive(String p_path, String p_base_path, zipFile p_zip) { + DirAccess *dir = DirAccess::open(p_path); + if (!dir) { + WARN_PRINT("Unable to open dir for zipping: " + p_path); + return; + } + dir->list_dir_begin(); + String cur = dir->get_next(); + while (!cur.empty()) { + String cs = p_path.plus_file(cur); + if (cur == "." || cur == ".." || cur == ".import") { + // Skip + } else if (dir->current_is_dir()) { + String path = cs.replace_first(p_base_path, "") + "/"; + zipOpenNewFileInZip(p_zip, + path.utf8().get_data(), + NULL, + NULL, + 0, + NULL, + 0, + NULL, + Z_DEFLATED, + Z_DEFAULT_COMPRESSION); + zipCloseFileInZip(p_zip); + _zip_recursive(cs, p_base_path, p_zip); + } else { + _zip_file(cs, p_base_path, p_zip); + } + cur = dir->get_next(); + } +} +#endif diff --git a/platform/javascript/api/javascript_tools_editor_plugin.h b/platform/javascript/api/javascript_tools_editor_plugin.h new file mode 100644 index 0000000000..cc09fa4cd3 --- /dev/null +++ b/platform/javascript/api/javascript_tools_editor_plugin.h @@ -0,0 +1,62 @@ +/*************************************************************************/ +/* javascript_tools_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 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 JAVASCRIPT_TOOLS_EDITOR_PLUGIN_H +#define JAVASCRIPT_TOOLS_EDITOR_PLUGIN_H + +#if defined(TOOLS_ENABLED) && defined(JAVASCRIPT_ENABLED) +#include "core/io/zip_io.h" +#include "editor/editor_plugin.h" + +class JavaScriptToolsEditorPlugin : public EditorPlugin { + GDCLASS(JavaScriptToolsEditorPlugin, EditorPlugin); + +private: + void _zip_file(String p_path, String p_base_path, zipFile p_zip); + void _zip_recursive(String p_path, String p_base_path, zipFile p_zip); + +protected: + static void _bind_methods(); + + void _download_zip(Variant p_v); + +public: + static void initialize(); + + JavaScriptToolsEditorPlugin(EditorNode *p_editor); +}; +#else +class JavaScriptToolsEditorPlugin { +public: + static void initialize() {} +}; +#endif + +#endif // JAVASCRIPT_TOOLS_EDITOR_PLUGIN_H diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py index 4b5890545f..8f2961b33d 100644 --- a/platform/javascript/detect.py +++ b/platform/javascript/detect.py @@ -135,7 +135,7 @@ def configure(env): env.Append(CPPDEFINES=["PTHREAD_NO_RENAME"]) env.Append(CCFLAGS=["-s", "USE_PTHREADS=1"]) env.Append(LINKFLAGS=["-s", "USE_PTHREADS=1"]) - env.Append(LINKFLAGS=["-s", "PTHREAD_POOL_SIZE=4"]) + env.Append(LINKFLAGS=["-s", "PTHREAD_POOL_SIZE=8"]) env.Append(LINKFLAGS=["-s", "WASM_MEM_MAX=2048MB"]) env.extra_suffix = ".threads" + env.extra_suffix else: diff --git a/platform/javascript/engine/engine.js b/platform/javascript/engine/engine.js index adcd919a6b..05a11701c0 100644 --- a/platform/javascript/engine/engine.js +++ b/platform/javascript/engine/engine.js @@ -121,6 +121,7 @@ Function('return this')()['Engine'] = (function() { me.rtenv['noExitRuntime'] = true; me.rtenv['onExecute'] = me.onExecute; me.rtenv['onExit'] = function(code) { + me.rtenv['deinitFS'](); if (me.onExit) me.onExit(code); me.rtenv = null; @@ -227,6 +228,12 @@ Function('return this')()['Engine'] = (function() { this.persistentPaths = persistentPaths; }; + Engine.prototype.requestQuit = function() { + if (this.rtenv) { + this.rtenv['request_quit'](); + } + }; + // Closure compiler exported engine methods. /** @export */ Engine['isWebGLAvailable'] = Utils.isWebGLAvailable; @@ -249,5 +256,6 @@ Function('return this')()['Engine'] = (function() { Engine.prototype['setOnExit'] = Engine.prototype.setOnExit; Engine.prototype['copyToFS'] = Engine.prototype.copyToFS; Engine.prototype['setPersistentPaths'] = Engine.prototype.setPersistentPaths; + Engine.prototype['requestQuit'] = Engine.prototype.requestQuit; return Engine; })(); diff --git a/platform/javascript/native/utils.js b/platform/javascript/native/utils.js index 0b3698fd86..8d0beba454 100644 --- a/platform/javascript/native/utils.js +++ b/platform/javascript/native/utils.js @@ -29,8 +29,7 @@ /*************************************************************************/ Module['initFS'] = function(persistentPaths) { - FS.mkdir('/userfs'); - FS.mount(IDBFS, {}, '/userfs'); + Module.mount_points = ['/userfs'].concat(persistentPaths); function createRecursive(dir) { try { @@ -43,13 +42,14 @@ Module['initFS'] = function(persistentPaths) { } } - persistentPaths.forEach(function(path) { + Module.mount_points.forEach(function(path) { createRecursive(path); FS.mount(IDBFS, {}, path); }); return new Promise(function(resolve, reject) { FS.syncfs(true, function(err) { if (err) { + Module.mount_points = []; Module.idbfs = false; console.log("IndexedDB not available: " + err.message); } else { @@ -60,6 +60,21 @@ Module['initFS'] = function(persistentPaths) { }); }; +Module['deinitFS'] = function() { + Module.mount_points.forEach(function(path) { + try { + FS.unmount(path); + } catch (e) { + console.log("Already unmounted", e); + } + if (Module.idbfs && IDBFS.dbs[path]) { + IDBFS.dbs[path].close(); + delete IDBFS.dbs[path]; + } + }); + Module.mount_points = []; +}; + Module['copyToFS'] = function(path, buffer) { var p = path.lastIndexOf("/"); var dir = "/"; diff --git a/platform/osx/detect.py b/platform/osx/detect.py index 6fc1dc65af..50e9bd2653 100644 --- a/platform/osx/detect.py +++ b/platform/osx/detect.py @@ -139,7 +139,8 @@ def configure(env): env.Append(CPPDEFINES=["__MACPORTS__"]) # hack to fix libvpx MM256_BROADCASTSI128_SI256 define if env["CXX"] == "clang++": - env.Append(CPPDEFINES=["TYPED_METHOD_BIND"]) + # This should now work with clang++, re-enable if there are issues + # env.Append(CPPDEFINES=["TYPED_METHOD_BIND"]) env["CC"] = "clang" env["LINK"] = "clang++" diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm index cd5b71890c..1ad7117b39 100644 --- a/platform/osx/display_server_osx.mm +++ b/platform/osx/display_server_osx.mm @@ -63,6 +63,8 @@ #define DS_OSX ((DisplayServerOSX *)(DisplayServerOSX::get_singleton())) +static bool ignore_momentum_scroll = false; + static void _get_key_modifier_state(unsigned int p_osx_state, Ref<InputEventWithModifiers> r_state) { r_state->set_shift((p_osx_state & NSEventModifierFlagShift)); r_state->set_control((p_osx_state & NSEventModifierFlagControl)); @@ -1304,6 +1306,8 @@ static int remapKey(unsigned int key, unsigned int state) { ERR_FAIL_COND(!DS_OSX->windows.has(window_id)); DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id]; + ignore_momentum_scroll = true; + // Ignore all input if IME input is in progress if (!imeInputEventInProgress) { NSString *characters = [event characters]; @@ -1348,6 +1352,8 @@ static int remapKey(unsigned int key, unsigned int state) { } - (void)flagsChanged:(NSEvent *)event { + ignore_momentum_scroll = true; + // Ignore all input if IME input is in progress if (!imeInputEventInProgress) { DisplayServerOSX::KeyEvent ke; @@ -1507,6 +1513,14 @@ inline void sendPanEvent(DisplayServer::WindowID window_id, double dx, double dy deltaY *= 0.03; } + if ([event momentumPhase] != NSEventPhaseNone) { + if (ignore_momentum_scroll) { + return; + } + } else { + ignore_momentum_scroll = false; + } + if ([event phase] != NSEventPhaseNone || [event momentumPhase] != NSEventPhaseNone) { sendPanEvent(window_id, deltaX, deltaY, [event modifierFlags]); } else { diff --git a/platform/uwp/detect.py b/platform/uwp/detect.py index 6e57dbbf64..2af7803749 100644 --- a/platform/uwp/detect.py +++ b/platform/uwp/detect.py @@ -81,7 +81,7 @@ def configure(env): vc_base_path = os.environ["VCTOOLSINSTALLDIR"] if "VCTOOLSINSTALLDIR" in os.environ else os.environ["VCINSTALLDIR"] # Force to use Unicode encoding - env.Append(MSVC_FLAGS=["/utf-8"]) + env.AppendUnique(CCFLAGS=["/utf-8"]) # ANGLE angle_root = os.getenv("ANGLE_SRC_PATH") diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp index 219174b509..e9e536837f 100644 --- a/platform/uwp/export/export.cpp +++ b/platform/uwp/export/export.cpp @@ -29,11 +29,12 @@ /*************************************************************************/ #include "export.h" + #include "core/bind/core_bind.h" +#include "core/class_db.h" #include "core/crypto/crypto_core.h" #include "core/io/marshalls.h" #include "core/io/zip_io.h" -#include "core/object.h" #include "core/os/dir_access.h" #include "core/os/file_access.h" #include "core/project_settings.h" diff --git a/platform/windows/detect.py b/platform/windows/detect.py index d5474ace9c..489e45404f 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -223,7 +223,7 @@ def configure_msvc(env, manual_msvc_config): env.AppendUnique(CCFLAGS=["/MT", "/Gd", "/GR", "/nologo"]) # Force to use Unicode encoding - env.Append(MSVC_FLAGS=["/utf-8"]) + env.AppendUnique(CCFLAGS=["/utf-8"]) env.AppendUnique(CXXFLAGS=["/TP"]) # assume all sources are C++ if manual_msvc_config: # should be automatic if SCons found it if os.getenv("WindowsSdkDir") is not None: |