diff options
-rw-r--r-- | core/array.cpp | 4 | ||||
-rw-r--r-- | core/array.h | 1 | ||||
-rw-r--r-- | core/variant_call.cpp | 2 | ||||
-rw-r--r-- | doc/base/classes.xml | 2 | ||||
-rw-r--r-- | platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java | 172 | ||||
-rw-r--r-- | platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java | 335 | ||||
-rw-r--r-- | platform/windows/os_windows.cpp | 1 | ||||
-rw-r--r-- | scene/2d/parallax_layer.cpp | 19 | ||||
-rw-r--r-- | scene/gui/file_dialog.cpp | 19 | ||||
-rw-r--r-- | scene/gui/file_dialog.h | 2 | ||||
-rw-r--r-- | scene/main/http_request.cpp | 108 | ||||
-rw-r--r-- | scene/main/http_request.h | 13 | ||||
-rw-r--r-- | scene/main/viewport.cpp | 26 | ||||
-rw-r--r-- | scene/resources/curve.cpp | 6 | ||||
-rw-r--r-- | servers/physics/body_pair_sw.cpp | 8 | ||||
-rw-r--r-- | tools/editor/asset_library_editor_plugin.cpp | 19 | ||||
-rw-r--r-- | tools/editor/code_editor.cpp | 40 | ||||
-rw-r--r-- | tools/editor/code_editor.h | 3 | ||||
-rw-r--r-- | tools/editor/editor_plugin.cpp | 6 | ||||
-rw-r--r-- | tools/editor/editor_plugin.h | 3 | ||||
-rw-r--r-- | version.py | 2 |
21 files changed, 522 insertions, 269 deletions
diff --git a/core/array.cpp b/core/array.cpp index bb8e527304..23792f90fc 100644 --- a/core/array.cpp +++ b/core/array.cpp @@ -200,6 +200,10 @@ int Array::count(const Variant& p_value) const { return amount; } +bool Array::has(const Variant& p_value) const { + return _p->array.find(p_value, 0) != -1; +} + void Array::remove(int p_pos) { _p->array.remove(p_pos); diff --git a/core/array.h b/core/array.h index 096660653e..dfc902525c 100644 --- a/core/array.h +++ b/core/array.h @@ -75,6 +75,7 @@ public: int rfind(const Variant& p_value, int p_from=-1) const; int find_last(const Variant& p_value) const; int count(const Variant& p_value) const; + bool has(const Variant& p_value) const; void erase(const Variant& p_value); diff --git a/core/variant_call.cpp b/core/variant_call.cpp index 7da4cef115..7956c14c2c 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -474,6 +474,7 @@ static void _call_##m_type##_##m_method(Variant& r_ret,Variant& p_self,const Var VCALL_LOCALMEM2R(Array,rfind); VCALL_LOCALMEM1R(Array,find_last); VCALL_LOCALMEM1R(Array,count); + VCALL_LOCALMEM1R(Array,has); VCALL_LOCALMEM1(Array,erase); VCALL_LOCALMEM0(Array,sort); VCALL_LOCALMEM2(Array,sort_custom); @@ -1516,6 +1517,7 @@ _VariantCall::addfunc(Variant::m_vtype,Variant::m_ret,_SCS(#m_method),VCALL(m_cl ADDFUNC2(ARRAY,INT,Array,rfind,NIL,"what",INT,"from",varray(-1)); ADDFUNC1(ARRAY,INT,Array,find_last,NIL,"value",varray()); ADDFUNC1(ARRAY,INT,Array,count,NIL,"value",varray()); + ADDFUNC1(ARRAY,BOOL,Array,has,NIL,"value",varray()); ADDFUNC0(ARRAY,NIL,Array,pop_back,varray()); ADDFUNC0(ARRAY,NIL,Array,pop_front,varray()); ADDFUNC0(ARRAY,NIL,Array,sort,varray()); diff --git a/doc/base/classes.xml b/doc/base/classes.xml index 4c2a0fe7ba..d70b11b5ae 100644 --- a/doc/base/classes.xml +++ b/doc/base/classes.xml @@ -11281,7 +11281,7 @@ <argument index="1" name="title" type="String"> </argument> <description> - Add a control to the bottom panel (together with Output, Debug, Animation, etc). If your plugin is being removed, also make sure to remove your control by calling [method remove_control_from_bottom_panel]. + Add a control to the bottom panel (together with Output, Debug, Animation, etc). Returns a reference to the button added. It's up to you to hide/show the button when needed. If your plugin is being removed, also make sure to remove your control by calling [method remove_control_from_bottom_panel]. </description> </method> <method name="add_control_to_container"> diff --git a/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java b/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java index fde752acc9..b7de31ada3 100644 --- a/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java +++ b/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java @@ -28,99 +28,94 @@ /*************************************************************************/ package org.godotengine.godot; -import org.godotengine.godot.Dictionary; import android.app.Activity; import android.util.Log; +import org.godotengine.godot.payments.PaymentsManager; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + public class GodotPaymentV3 extends Godot.SingletonBase { private Godot activity; - private Integer purchaseCallbackId = 0; - private String accessToken; - private String purchaseValidationUrlPrefix; - private String transactionId; + private PaymentsManager mPaymentManager; + private Dictionary mSkuDetails = new Dictionary(); - public void purchase( String _sku, String _transactionId) { - final String sku = _sku; - final String transactionId = _transactionId; - activity.getPaymentsManager().setBaseSingleton(this); + public void purchase(final String sku, final String transactionId) { activity.runOnUiThread(new Runnable() { @Override public void run() { - activity.getPaymentsManager().requestPurchase(sku, transactionId); + mPaymentManager.requestPurchase(sku, transactionId); } }); } - -/* public string requestPurchasedTicket(){ - activity.getPaymentsManager() - } -*/ - static public Godot.SingletonBase initialize(Activity p_activity) { + static public Godot.SingletonBase initialize(Activity p_activity) { - return new GodotPaymentV3(p_activity); - } + return new GodotPaymentV3(p_activity); + } - public GodotPaymentV3(Activity p_activity) { - registerClass("GodotPayments", new String[] {"purchase", "setPurchaseCallbackId", "setPurchaseValidationUrlPrefix", "setTransactionId", "getSignature", "consumeUnconsumedPurchases", "requestPurchased", "setAutoConsume", "consume"}); - activity=(Godot) p_activity; + registerClass("GodotPayments", new String[]{"purchase", "setPurchaseCallbackId", "setPurchaseValidationUrlPrefix", "setTransactionId", "getSignature", "consumeUnconsumedPurchases", "requestPurchased", "setAutoConsume", "consume", "querySkuDetails"}); + activity = (Godot) p_activity; + mPaymentManager = activity.getPaymentsManager(); + mPaymentManager.setBaseSingleton(this); } - public void consumeUnconsumedPurchases(){ - activity.getPaymentsManager().setBaseSingleton(this); + public void consumeUnconsumedPurchases() { activity.runOnUiThread(new Runnable() { @Override public void run() { - activity.getPaymentsManager().consumeUnconsumedPurchases(); + mPaymentManager.consumeUnconsumedPurchases(); } }); } private String signature; - public String getSignature(){ - return this.signature; + + public String getSignature() { + return this.signature; } - - - public void callbackSuccess(String ticket, String signature, String sku){ -// Log.d(this.getClass().getName(), "PRE-Send callback to purchase success"); + + public void callbackSuccess(String ticket, String signature, String sku) { GodotLib.callobject(purchaseCallbackId, "purchase_success", new Object[]{ticket, signature, sku}); -// Log.d(this.getClass().getName(), "POST-Send callback to purchase success"); -} + } + + public void callbackSuccessProductMassConsumed(String ticket, String signature, String sku) { + Log.d(this.getClass().getName(), "callbackSuccessProductMassConsumed > " + ticket + "," + signature + "," + sku); + GodotLib.calldeferred(purchaseCallbackId, "consume_success", new Object[]{ticket, signature, sku}); + } - public void callbackSuccessProductMassConsumed(String ticket, String signature, String sku){ -// Log.d(this.getClass().getName(), "PRE-Send callback to consume success"); - Log.d(this.getClass().getName(), "callbackSuccessProductMassConsumed > "+ticket+","+signature+","+sku); - GodotLib.calldeferred(purchaseCallbackId, "consume_success", new Object[]{ticket, signature, sku}); -// Log.d(this.getClass().getName(), "POST-Send callback to consume success"); + public void callbackSuccessNoUnconsumedPurchases() { + GodotLib.calldeferred(purchaseCallbackId, "consume_not_required", new Object[]{}); } - public void callbackSuccessNoUnconsumedPurchases(){ - GodotLib.calldeferred(purchaseCallbackId, "no_validation_required", new Object[]{}); + public void callbackFailConsume() { + GodotLib.calldeferred(purchaseCallbackId, "consume_fail", new Object[]{}); } - - public void callbackFail(){ + + public void callbackFail() { GodotLib.calldeferred(purchaseCallbackId, "purchase_fail", new Object[]{}); -// GodotLib.callobject(purchaseCallbackId, "purchase_fail", new Object[]{}); } - - public void callbackCancel(){ + + public void callbackCancel() { GodotLib.calldeferred(purchaseCallbackId, "purchase_cancel", new Object[]{}); -// GodotLib.callobject(purchaseCallbackId, "purchase_cancel", new Object[]{}); } - - public void callbackAlreadyOwned(String sku){ + + public void callbackAlreadyOwned(String sku) { GodotLib.calldeferred(purchaseCallbackId, "purchase_owned", new Object[]{sku}); } - + public int getPurchaseCallbackId() { return purchaseCallbackId; } @@ -129,11 +124,11 @@ public class GodotPaymentV3 extends Godot.SingletonBase { this.purchaseCallbackId = purchaseCallbackId; } - public String getPurchaseValidationUrlPrefix(){ - return this.purchaseValidationUrlPrefix ; + public String getPurchaseValidationUrlPrefix() { + return this.purchaseValidationUrlPrefix; } - public void setPurchaseValidationUrlPrefix(String url){ + public void setPurchaseValidationUrlPrefix(String url) { this.purchaseValidationUrlPrefix = url; } @@ -144,39 +139,80 @@ public class GodotPaymentV3 extends Godot.SingletonBase { public void setAccessToken(String accessToken) { this.accessToken = accessToken; } - - public void setTransactionId(String transactionId){ + + public void setTransactionId(String transactionId) { this.transactionId = transactionId; } - - public String getTransactionId(){ + + public String getTransactionId() { return this.transactionId; } - + // request purchased items are not consumed - public void requestPurchased(){ - activity.getPaymentsManager().setBaseSingleton(this); + public void requestPurchased() { activity.runOnUiThread(new Runnable() { @Override public void run() { - activity.getPaymentsManager().requestPurchased(); + mPaymentManager.requestPurchased(); } }); } - + // callback for requestPurchased() - public void callbackPurchased(String receipt, String signature, String sku){ + public void callbackPurchased(String receipt, String signature, String sku) { GodotLib.calldeferred(purchaseCallbackId, "has_purchased", new Object[]{receipt, signature, sku}); } - + // consume item automatically after purchase. default is true. - public void setAutoConsume(boolean autoConsume){ - activity.getPaymentsManager().setAutoConsume(autoConsume); + public void setAutoConsume(boolean autoConsume) { + mPaymentManager.setAutoConsume(autoConsume); } - + // consume a specific item - public void consume(String sku){ - activity.getPaymentsManager().consume(sku); + public void consume(String sku) { + mPaymentManager.consume(sku); + } + + // query in app item detail info + public void querySkuDetails(String[] list) { + List<String> nKeys = Arrays.asList(list); + List<String> cKeys = Arrays.asList(mSkuDetails.get_keys()); + ArrayList<String> fKeys = new ArrayList<String>(); + for (String key : nKeys) { + if (!cKeys.contains(key)) { + fKeys.add(key); + } + } + if (fKeys.size() > 0) { + mPaymentManager.querySkuDetails(fKeys.toArray(new String[0])); + } else { + completeSkuDetail(); + } + } + + public void addSkuDetail(String itemJson) { + JSONObject o = null; + try { + o = new JSONObject(itemJson); + Dictionary item = new Dictionary(); + item.put("type", o.optString("type")); + item.put("product_id", o.optString("productId")); + item.put("title", o.optString("title")); + item.put("description", o.optString("description")); + item.put("price", o.optString("price")); + item.put("price_currency_code", o.optString("price_currency_code")); + item.put("price_amount", 0.000001d * o.optLong("price_amount_micros")); + mSkuDetails.put(item.get("product_id").toString(), item); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + public void completeSkuDetail() { + GodotLib.calldeferred(purchaseCallbackId, "sku_details_complete", new Object[]{mSkuDetails}); + } + + public void errorSkuDetail(String errorMessage) { + GodotLib.calldeferred(purchaseCallbackId, "sku_details_error", new Object[]{errorMessage}); } } - diff --git a/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java b/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java index eb33b37ecc..753c0a6f93 100644 --- a/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java +++ b/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java @@ -28,283 +28,370 @@ /*************************************************************************/ package org.godotengine.godot.payments; -import java.util.ArrayList; -import java.util.List; - -import android.os.RemoteException; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.os.Bundle; import android.os.IBinder; +import android.os.RemoteException; +import android.text.TextUtils; import android.util.Log; -import android.os.Bundle; -import org.json.JSONException; -import org.json.JSONObject; -import org.json.JSONStringer; +import com.android.vending.billing.IInAppBillingService; -import org.godotengine.godot.Dictionary; import org.godotengine.godot.Godot; import org.godotengine.godot.GodotPaymentV3; -import com.android.vending.billing.IInAppBillingService; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Arrays; public class PaymentsManager { public static final int BILLING_RESPONSE_RESULT_OK = 0; public static final int REQUEST_CODE_FOR_PURCHASE = 0x1001; private static boolean auto_consume = true; - + private Activity activity; IInAppBillingService mService; - public void setActivity(Activity activity){ + public void setActivity(Activity activity) { this.activity = activity; } - public static PaymentsManager createManager(Activity activity){ + public static PaymentsManager createManager(Activity activity) { PaymentsManager manager = new PaymentsManager(activity); return manager; } - - private PaymentsManager(Activity activity){ + + private PaymentsManager(Activity activity) { this.activity = activity; } - - public PaymentsManager initService(){ + + public PaymentsManager initService() { Intent intent = new Intent("com.android.vending.billing.InAppBillingService.BIND"); intent.setPackage("com.android.vending"); activity.bindService( - intent, - mServiceConn, + intent, + mServiceConn, Context.BIND_AUTO_CREATE); return this; } - public void destroy(){ + public void destroy() { if (mService != null) { - activity.unbindService(mServiceConn); - } + activity.unbindService(mServiceConn); + } } - + ServiceConnection mServiceConn = new ServiceConnection() { - @Override - public void onServiceDisconnected(ComponentName name) { - mService = null; - } + @Override + public void onServiceDisconnected(ComponentName name) { + mService = null; + } - @Override - public void onServiceConnected(ComponentName name, IBinder service) { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { mService = IInAppBillingService.Stub.asInterface(service); - } + } }; - - public void requestPurchase(final String sku, String transactionId){ + + public void requestPurchase(final String sku, String transactionId) { new PurchaseTask(mService, Godot.getInstance()) { - + @Override protected void error(String message) { godotPaymentV3.callbackFail(); - + } - + @Override protected void canceled() { godotPaymentV3.callbackCancel(); } - + @Override protected void alreadyOwned() { godotPaymentV3.callbackAlreadyOwned(sku); } - + }.purchase(sku, transactionId); } - public void consumeUnconsumedPurchases(){ + public void consumeUnconsumedPurchases() { new ReleaseAllConsumablesTask(mService, activity) { - + @Override protected void success(String sku, String receipt, String signature, String token) { godotPaymentV3.callbackSuccessProductMassConsumed(receipt, signature, sku); } - + @Override protected void error(String message) { - godotPaymentV3.callbackFail(); - + Log.d("godot", "consumeUnconsumedPurchases :" + message); + godotPaymentV3.callbackFailConsume(); + } @Override protected void notRequired() { + Log.d("godot", "callbackSuccessNoUnconsumedPurchases :"); godotPaymentV3.callbackSuccessNoUnconsumedPurchases(); - + } }.consumeItAll(); } - - public void requestPurchased(){ - try{ + + public void requestPurchased() { + try { PaymentsCache pc = new PaymentsCache(Godot.getInstance()); -// Log.d("godot", "requestPurchased for " + activity.getPackageName()); - Bundle bundle = mService.getPurchases(3, activity.getPackageName(), "inapp",null); + String continueToken = null; -/* - for (String key : bundle.keySet()) { - Object value = bundle.get(key); - Log.d("godot", String.format("%s %s (%s)", key, value.toString(), value.getClass().getName())); - } -*/ - - if (bundle.getInt("RESPONSE_CODE") == 0){ + do { + Bundle bundle = mService.getPurchases(3, activity.getPackageName(), "inapp", continueToken); - final ArrayList<String> myPurchases = bundle.getStringArrayList("INAPP_PURCHASE_DATA_LIST"); - final ArrayList<String> mySignatures = bundle.getStringArrayList("INAPP_DATA_SIGNATURE_LIST"); - + if (bundle.getInt("RESPONSE_CODE") == 0) { - if (myPurchases == null || myPurchases.size() == 0){ -// Log.d("godot", "No purchases!"); - godotPaymentV3.callbackPurchased("", "", ""); - return; - } - -// Log.d("godot", "# products are purchased:" + myPurchases.size()); - for (int i=0;i<myPurchases.size();i++) - { - - try{ - String receipt = myPurchases.get(i); - JSONObject inappPurchaseData = new JSONObject(receipt); - String sku = inappPurchaseData.getString("productId"); - String token = inappPurchaseData.getString("purchaseToken"); - String signature = mySignatures.get(i); -// Log.d("godot", "purchased item:" + token + "\n" + receipt); - - pc.setConsumableValue("ticket_signautre", sku, signature); - pc.setConsumableValue("ticket", sku, receipt); - pc.setConsumableFlag("block", sku, true); - pc.setConsumableValue("token", sku, token); - - godotPaymentV3.callbackPurchased(receipt, signature, sku); - } catch (JSONException e) { + final ArrayList<String> myPurchases = bundle.getStringArrayList("INAPP_PURCHASE_DATA_LIST"); + final ArrayList<String> mySignatures = bundle.getStringArrayList("INAPP_DATA_SIGNATURE_LIST"); + + if (myPurchases == null || myPurchases.size() == 0) { + godotPaymentV3.callbackPurchased("", "", ""); + return; } - } - } - }catch(Exception e){ + for (int i = 0; i < myPurchases.size(); i++) { + + try { + String receipt = myPurchases.get(i); + JSONObject inappPurchaseData = new JSONObject(receipt); + String sku = inappPurchaseData.getString("productId"); + String token = inappPurchaseData.getString("purchaseToken"); + String signature = mySignatures.get(i); + + pc.setConsumableValue("ticket_signautre", sku, signature); + pc.setConsumableValue("ticket", sku, receipt); + pc.setConsumableFlag("block", sku, true); + pc.setConsumableValue("token", sku, token); + + godotPaymentV3.callbackPurchased(receipt, signature, sku); + } catch (JSONException e) { + } + } + } + continueToken = bundle.getString("INAPP_CONTINUATION_TOKEN"); + Log.d("godot", "continue token = " + continueToken); + } while (!TextUtils.isEmpty(continueToken)); + } catch (Exception e) { Log.d("godot", "Error requesting purchased products:" + e.getClass().getName() + ":" + e.getMessage()); } } - + public void processPurchaseResponse(int resultCode, Intent data) { - new HandlePurchaseTask(activity){ + new HandlePurchaseTask(activity) { @Override protected void success(final String sku, final String signature, final String ticket) { godotPaymentV3.callbackSuccess(ticket, signature, sku); - if (auto_consume){ + if (auto_consume) { new ConsumeTask(mService, activity) { - + @Override protected void success(String ticket) { -// godotPaymentV3.callbackSuccess(""); } - + @Override protected void error(String message) { godotPaymentV3.callbackFail(); - + } }.consume(sku); } - -// godotPaymentV3.callbackSuccess(new PaymentsCache(activity).getConsumableValue("ticket", sku),signature); -// godotPaymentV3.callbackSuccess(ticket); - //validatePurchase(purchaseToken, sku); } @Override protected void error(String message) { godotPaymentV3.callbackFail(); - } @Override protected void canceled() { godotPaymentV3.callbackCancel(); - } }.handlePurchaseRequest(resultCode, data); } - - public void validatePurchase(String purchaseToken, final String sku){ - - new ValidateTask(activity, godotPaymentV3){ + + public void validatePurchase(String purchaseToken, final String sku) { + + new ValidateTask(activity, godotPaymentV3) { @Override protected void success() { - + new ConsumeTask(mService, activity) { - + @Override protected void success(String ticket) { godotPaymentV3.callbackSuccess(ticket, null, sku); - } - + @Override protected void error(String message) { godotPaymentV3.callbackFail(); - } }.consume(sku); - + } @Override protected void error(String message) { godotPaymentV3.callbackFail(); - } @Override protected void canceled() { godotPaymentV3.callbackCancel(); - } }.validatePurchase(sku); } - - public void setAutoConsume(boolean autoConsume){ + + public void setAutoConsume(boolean autoConsume) { auto_consume = autoConsume; } - - public void consume(final String sku){ + + public void consume(final String sku) { new ConsumeTask(mService, activity) { - + @Override protected void success(String ticket) { godotPaymentV3.callbackSuccessProductMassConsumed(ticket, "", sku); - } - + @Override protected void error(String message) { - godotPaymentV3.callbackFail(); - + godotPaymentV3.callbackFailConsume(); } }.consume(sku); } - + + // Workaround to bug where sometimes response codes come as Long instead of Integer + int getResponseCodeFromBundle(Bundle b) { + Object o = b.get("RESPONSE_CODE"); + if (o == null) { + //logDebug("Bundle with null response code, assuming OK (known issue)"); + return BILLING_RESPONSE_RESULT_OK; + } else if (o instanceof Integer) return ((Integer) o).intValue(); + else if (o instanceof Long) return (int) ((Long) o).longValue(); + else { + //logError("Unexpected type for bundle response code."); + //logError(o.getClass().getName()); + throw new RuntimeException("Unexpected type for bundle response code: " + o.getClass().getName()); + } + } + + /** + * Returns a human-readable description for the given response code. + * + * @param code The response code + * @return A human-readable string explaining the result code. + * It also includes the result code numerically. + */ + public static String getResponseDesc(int code) { + String[] iab_msgs = ("0:OK/1:User Canceled/2:Unknown/" + + "3:Billing Unavailable/4:Item unavailable/" + + "5:Developer Error/6:Error/7:Item Already Owned/" + + "8:Item not owned").split("/"); + String[] iabhelper_msgs = ("0:OK/-1001:Remote exception during initialization/" + + "-1002:Bad response received/" + + "-1003:Purchase signature verification failed/" + + "-1004:Send intent failed/" + + "-1005:User cancelled/" + + "-1006:Unknown purchase response/" + + "-1007:Missing token/" + + "-1008:Unknown error/" + + "-1009:Subscriptions not available/" + + "-1010:Invalid consumption attempt").split("/"); + + if (code <= -1000) { + int index = -1000 - code; + if (index >= 0 && index < iabhelper_msgs.length) return iabhelper_msgs[index]; + else return String.valueOf(code) + ":Unknown IAB Helper Error"; + } else if (code < 0 || code >= iab_msgs.length) + return String.valueOf(code) + ":Unknown"; + else + return iab_msgs[code]; + } + + public void querySkuDetails(final String[] list) { + (new Thread(new Runnable() { + @Override + public void run() { + ArrayList<String> skuList = new ArrayList<String>(Arrays.asList(list)); + if (skuList.size() == 0) { + return; + } + // Split the sku list in blocks of no more than 20 elements. + ArrayList<ArrayList<String>> packs = new ArrayList<ArrayList<String>>(); + ArrayList<String> tempList; + int n = skuList.size() / 20; + int mod = skuList.size() % 20; + for (int i = 0; i < n; i++) { + tempList = new ArrayList<String>(); + for (String s : skuList.subList(i * 20, i * 20 + 20)) { + tempList.add(s); + } + packs.add(tempList); + } + if (mod != 0) { + tempList = new ArrayList<String>(); + for (String s : skuList.subList(n * 20, n * 20 + mod)) { + tempList.add(s); + } + packs.add(tempList); + + for (ArrayList<String> skuPartList : packs) { + Bundle querySkus = new Bundle(); + querySkus.putStringArrayList("ITEM_ID_LIST", skuPartList); + Bundle skuDetails = null; + try { + skuDetails = mService.getSkuDetails(3, activity.getPackageName(), "inapp", querySkus); + if (!skuDetails.containsKey("DETAILS_LIST")) { + int response = getResponseCodeFromBundle(skuDetails); + if (response != BILLING_RESPONSE_RESULT_OK) { + godotPaymentV3.errorSkuDetail(getResponseDesc(response)); + } else { + godotPaymentV3.errorSkuDetail("No error but no detail list."); + } + return; + } + + ArrayList<String> responseList = skuDetails.getStringArrayList("DETAILS_LIST"); + + for (String thisResponse : responseList) { + Log.d("godot", "response = "+thisResponse); + godotPaymentV3.addSkuDetail(thisResponse); + } + } catch (RemoteException e) { + e.printStackTrace(); + godotPaymentV3.errorSkuDetail("RemoteException error!"); + } + } + godotPaymentV3.completeSkuDetail(); + } + } + })).start(); + } + private GodotPaymentV3 godotPaymentV3; - + public void setBaseSingleton(GodotPaymentV3 godotPaymentV3) { this.godotPaymentV3 = godotPaymentV3; } } - diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 6aee0d2399..a3858fe641 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -1476,6 +1476,7 @@ Point2 OS_Windows::get_window_position() const{ } void OS_Windows::set_window_position(const Point2& p_position){ + if (video_mode.fullscreen) return; RECT r; GetWindowRect(hWnd,&r); MoveWindow(hWnd,p_position.x,p_position.y,r.right-r.left,r.bottom-r.top,TRUE); diff --git a/scene/2d/parallax_layer.cpp b/scene/2d/parallax_layer.cpp index e9378b1d02..05136de5d6 100644 --- a/scene/2d/parallax_layer.cpp +++ b/scene/2d/parallax_layer.cpp @@ -123,26 +123,15 @@ void ParallaxLayer::set_base_offset_and_scale(const Point2& p_offset,float p_sca Point2 new_ofs = ((orig_offset+p_offset)*motion_scale)*p_scale+motion_offset; if (mirroring.x) { - - while( new_ofs.x>=0) { - new_ofs.x -= mirroring.x*p_scale; - } - while(new_ofs.x < -mirroring.x*p_scale) { - new_ofs.x += mirroring.x*p_scale; - } + double den = mirroring.x*p_scale; + new_ofs.x -= den*ceil(new_ofs.x/den); } if (mirroring.y) { - - while( new_ofs.y>=0) { - new_ofs.y -= mirroring.y*p_scale; - } - while(new_ofs.y < -mirroring.y*p_scale) { - new_ofs.y += mirroring.y*p_scale; - } + double den = mirroring.y*p_scale; + new_ofs.y -= den*ceil(new_ofs.y/den); } - set_pos(new_ofs); set_scale(Vector2(1,1)*p_scale); diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index d335399caa..6b43425edc 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -475,7 +475,7 @@ void FileDialog::update_filters() { String flt=filters[i].get_slice(";",0).strip_edges(); String desc=filters[i].get_slice(";",1).strip_edges(); if (desc.length()) - filter->add_item(desc+" ( "+flt+" )"); + filter->add_item(String(XL_MESSAGE(desc))+" ( "+flt+" )"); else filter->add_item("( "+flt+" )"); } @@ -498,6 +498,16 @@ void FileDialog::add_filter(const String& p_filter) { } +void FileDialog::set_filters(const Vector<String>& p_filters){ + filters=p_filters; + update_filters(); + invalidate(); +} + +Vector<String> FileDialog::get_filters() const{ + return filters; +} + String FileDialog::get_current_dir() const { return dir->get_text(); @@ -686,6 +696,8 @@ void FileDialog::_bind_methods() { ObjectTypeDB::bind_method(_MD("clear_filters"),&FileDialog::clear_filters); ObjectTypeDB::bind_method(_MD("add_filter","filter"),&FileDialog::add_filter); + ObjectTypeDB::bind_method(_MD("set_filters","filters"),&FileDialog::set_filters); + ObjectTypeDB::bind_method(_MD("get_filters"),&FileDialog::get_filters); ObjectTypeDB::bind_method(_MD("get_current_dir"),&FileDialog::get_current_dir); ObjectTypeDB::bind_method(_MD("get_current_file"),&FileDialog::get_current_file); ObjectTypeDB::bind_method(_MD("get_current_path"),&FileDialog::get_current_path); @@ -722,6 +734,11 @@ void FileDialog::_bind_methods() { BIND_CONSTANT( ACCESS_USERDATA ); BIND_CONSTANT( ACCESS_FILESYSTEM ); + ADD_PROPERTY( PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Open one,Open many,Open folder,Open any,Save"),_SCS("set_mode"),_SCS("get_mode") ); + ADD_PROPERTY( PropertyInfo(Variant::INT, "access", PROPERTY_HINT_ENUM, "Resources,User data,File system"),_SCS("set_access"),_SCS("get_access") ); + ADD_PROPERTY( PropertyInfo(Variant::STRING_ARRAY, "filters"),_SCS("set_filters"),_SCS("get_filters") ); + ADD_PROPERTY( PropertyInfo(Variant::BOOL, "show_hidden_files"),_SCS("set_show_hidden_files"),_SCS("is_showing_hidden_files") ); + } diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h index 1fcf8387ce..150b24cb3f 100644 --- a/scene/gui/file_dialog.h +++ b/scene/gui/file_dialog.h @@ -131,6 +131,8 @@ public: void clear_filters(); void add_filter(const String& p_filter); + void set_filters(const Vector<String>& p_filters); + Vector<String> get_filters() const; void set_enable_multiple_selection(bool p_enable); Vector<String> get_selected_files() const; diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp index 040d509286..9b79af32bf 100644 --- a/scene/main/http_request.cpp +++ b/scene/main/http_request.cpp @@ -105,6 +105,9 @@ Error HTTPRequest::request(const String& p_url, const Vector<String>& p_custom_h } Error err = _parse_url(p_url); + if (err) + return err; + validate_ssl=p_ssl_validate_domain; bool has_user_agent=false; @@ -127,19 +130,52 @@ Error HTTPRequest::request(const String& p_url, const Vector<String>& p_custom_h headers.push_back("Accept: */*"); } + requesting=true; + + if (use_threads) { - err = _request(); + thread_done=false; + thread_request_quit=false; + client->set_blocking_mode(true); + thread=Thread::create(_thread_func,this); + } else { + client->set_blocking_mode(false); + err = _request(); + if (err!=OK) { + call_deferred("_request_done",RESULT_CANT_CONNECT,0,StringArray(),ByteArray()); + return ERR_CANT_CONNECT; + } - if (err==OK) { set_process(true); - requesting=true; + } - return err; + return OK; } +void HTTPRequest::_thread_func(void *p_userdata) { + + HTTPRequest *hr = (HTTPRequest*)p_userdata; + + Error err = hr->_request(); + + if (err!=OK) { + hr->call_deferred("_request_done",RESULT_CANT_CONNECT,0,StringArray(),ByteArray()); + } else { + while(!hr->thread_request_quit) { + + bool exit = hr->_update_connection(); + if (exit) + break; + OS::get_singleton()->delay_usec(1); + } + } + + hr->thread_done=true; +} + void HTTPRequest::cancel_request() { if (!requesting) @@ -147,6 +183,11 @@ void HTTPRequest::cancel_request() { if (!use_threads) { set_process(false); + } else { + thread_request_quit=true; + Thread::wait_to_finish(thread); + memdelete(thread); + thread=NULL; } if (file) { @@ -167,7 +208,7 @@ void HTTPRequest::cancel_request() { bool HTTPRequest::_handle_response(bool *ret_value) { if (!client->has_response()) { - call_deferred("emit_signal","request_completed",RESULT_NO_RESPONSE,0,StringArray(),ByteArray()); + call_deferred("_request_done",RESULT_NO_RESPONSE,0,StringArray(),ByteArray()); *ret_value=true; return true; } @@ -187,7 +228,7 @@ bool HTTPRequest::_handle_response(bool *ret_value) { //redirect if (max_redirects>=0 && redirections>=max_redirects) { - call_deferred("emit_signal","request_completed",RESULT_REDIRECT_LIMIT_REACHED,response_code,response_headers,ByteArray()); + call_deferred("_request_done",RESULT_REDIRECT_LIMIT_REACHED,response_code,response_headers,ByteArray()); *ret_value=true; return true; } @@ -239,7 +280,8 @@ bool HTTPRequest::_update_connection() { switch( client->get_status() ) { case HTTPClient::STATUS_DISCONNECTED: { - return true; //end it, since it's doing something + call_deferred("_request_done",RESULT_CANT_CONNECT,0,StringArray(),ByteArray()); + return true; //end it, since it's doing something } break; case HTTPClient::STATUS_RESOLVING: { client->poll(); @@ -247,7 +289,7 @@ bool HTTPRequest::_update_connection() { return false; } break; case HTTPClient::STATUS_CANT_RESOLVE: { - call_deferred("emit_signal","request_completed",RESULT_CANT_RESOLVE,0,StringArray(),ByteArray()); + call_deferred("_request_done",RESULT_CANT_RESOLVE,0,StringArray(),ByteArray()); return true; } break; @@ -258,7 +300,7 @@ bool HTTPRequest::_update_connection() { } break; //connecting to ip case HTTPClient::STATUS_CANT_CONNECT: { - call_deferred("emit_signal","request_completed",RESULT_CANT_CONNECT,0,StringArray(),ByteArray()); + call_deferred("_request_done",RESULT_CANT_CONNECT,0,StringArray(),ByteArray()); return true; } break; @@ -276,17 +318,17 @@ bool HTTPRequest::_update_connection() { return ret_value; - call_deferred("emit_signal","request_completed",RESULT_SUCCESS,response_code,response_headers,ByteArray()); + call_deferred("_request_done",RESULT_SUCCESS,response_code,response_headers,ByteArray()); return true; } if (got_response && body_len<0) { //chunked transfer is done - call_deferred("emit_signal","request_completed",RESULT_SUCCESS,response_code,response_headers,body); + call_deferred("_request_done",RESULT_SUCCESS,response_code,response_headers,body); return true; } - call_deferred("emit_signal","request_completed",RESULT_CHUNKED_BODY_SIZE_MISMATCH,response_code,response_headers,ByteArray()); + call_deferred("_request_done",RESULT_CHUNKED_BODY_SIZE_MISMATCH,response_code,response_headers,ByteArray()); return true; //request migh have been done } else { @@ -294,7 +336,7 @@ bool HTTPRequest::_update_connection() { Error err = client->request(HTTPClient::METHOD_GET,request_string,headers); if (err!=OK) { - call_deferred("emit_signal","request_completed",RESULT_CONNECTION_ERROR,0,StringArray(),ByteArray()); + call_deferred("_request_done",RESULT_CONNECTION_ERROR,0,StringArray(),ByteArray()); return true; } @@ -320,7 +362,7 @@ bool HTTPRequest::_update_connection() { if (!client->is_response_chunked() && client->get_response_body_length()==0) { - call_deferred("emit_signal","request_completed",RESULT_SUCCESS,response_code,response_headers,ByteArray()); + call_deferred("_request_done",RESULT_SUCCESS,response_code,response_headers,ByteArray()); return true; } @@ -331,7 +373,7 @@ bool HTTPRequest::_update_connection() { body_len=client->get_response_body_length(); if (body_size_limit>=0 && body_len>body_size_limit) { - call_deferred("emit_signal","request_completed",RESULT_BODY_SIZE_LIMIT_EXCEEDED,response_code,response_headers,ByteArray()); + call_deferred("_request_done",RESULT_BODY_SIZE_LIMIT_EXCEEDED,response_code,response_headers,ByteArray()); return true; } } @@ -340,7 +382,8 @@ bool HTTPRequest::_update_connection() { file=FileAccess::open(download_to_file,FileAccess::WRITE); if (!file) { - call_deferred("emit_signal","request_completed",RESULT_DOWNLOAD_FILE_CANT_OPEN,response_code,response_headers,ByteArray()); + call_deferred("_request_done",RESULT_DOWNLOAD_FILE_CANT_OPEN,response_code,response_headers,ByteArray()); + return true; } } } @@ -356,7 +399,7 @@ bool HTTPRequest::_update_connection() { ByteArray::Read r=chunk.read(); file->store_buffer(r.ptr(),chunk.size()); if (file->get_error()!=OK) { - call_deferred("emit_signal","request_completed",RESULT_DOWNLOAD_FILE_WRITE_ERROR,response_code,response_headers,ByteArray()); + call_deferred("_request_done",RESULT_DOWNLOAD_FILE_WRITE_ERROR,response_code,response_headers,ByteArray()); return true; } } else { @@ -364,18 +407,18 @@ bool HTTPRequest::_update_connection() { } if (body_size_limit>=0 && downloaded>body_size_limit) { - call_deferred("emit_signal","request_completed",RESULT_BODY_SIZE_LIMIT_EXCEEDED,response_code,response_headers,ByteArray()); + call_deferred("_request_done",RESULT_BODY_SIZE_LIMIT_EXCEEDED,response_code,response_headers,ByteArray()); return true; } if (body_len>=0) { if (downloaded==body_len) { - call_deferred("emit_signal","request_completed",RESULT_SUCCESS,response_code,response_headers,body); + call_deferred("_request_done",RESULT_SUCCESS,response_code,response_headers,body); return true; } /*if (body.size()>=body_len) { - call_deferred("emit_signal","request_completed",RESULT_BODY_SIZE_MISMATCH,response_code,response_headers,ByteArray()); + call_deferred("_request_done",RESULT_BODY_SIZE_MISMATCH,response_code,response_headers,ByteArray()); return true; }*/ } @@ -384,11 +427,11 @@ bool HTTPRequest::_update_connection() { } break; // request resulted in body: { } break which must be read case HTTPClient::STATUS_CONNECTION_ERROR: { - call_deferred("emit_signal","request_completed",RESULT_CONNECTION_ERROR,0,StringArray(),ByteArray()); + call_deferred("_request_done",RESULT_CONNECTION_ERROR,0,StringArray(),ByteArray()); return true; } break; case HTTPClient::STATUS_SSL_HANDSHAKE_ERROR: { - call_deferred("emit_signal","request_completed",RESULT_SSL_HANDSHAKE_ERROR,0,StringArray(),ByteArray()); + call_deferred("_request_done",RESULT_SSL_HANDSHAKE_ERROR,0,StringArray(),ByteArray()); return true; } break; @@ -397,17 +440,35 @@ bool HTTPRequest::_update_connection() { ERR_FAIL_V(false); } + +void HTTPRequest::_request_done(int p_status, int p_code, const StringArray& headers, const ByteArray& p_data) { + + + cancel_request(); + emit_signal("request_completed",p_status,p_code,headers,p_data); +} + + void HTTPRequest::_notification(int p_what) { if (p_what==NOTIFICATION_PROCESS) { + if (use_threads) + return; bool done = _update_connection(); if (done) { set_process(false); + //cancel_request(); called from _request done now + } + } + + if (p_what==NOTIFICATION_EXIT_TREE) { + if (requesting) { cancel_request(); } } + } void HTTPRequest::set_use_threads(bool p_use) { @@ -491,6 +552,7 @@ void HTTPRequest::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_body_size"),&HTTPRequest::get_body_size); ObjectTypeDB::bind_method(_MD("_redirect_request"),&HTTPRequest::_redirect_request); + ObjectTypeDB::bind_method(_MD("_request_done"),&HTTPRequest::_request_done); ADD_PROPERTY(PropertyInfo(Variant::BOOL,"use_threads"),_SCS("set_use_threads"),_SCS("is_using_threads")); ADD_PROPERTY(PropertyInfo(Variant::INT,"body_size_limit",PROPERTY_HINT_RANGE,"-1,2000000000"),_SCS("set_body_size_limit"),_SCS("get_body_size_limit")); @@ -517,6 +579,7 @@ void HTTPRequest::_bind_methods() { HTTPRequest::HTTPRequest() { + thread=NULL; port=80; redirections=0; @@ -530,6 +593,7 @@ HTTPRequest::HTTPRequest() requesting=false; client.instance(); use_threads=false; + thread_done=false; body_size_limit=-1; file=NULL; status=HTTPClient::STATUS_DISCONNECTED; diff --git a/scene/main/http_request.h b/scene/main/http_request.h index 7c3ccb2eb9..8dd433982b 100644 --- a/scene/main/http_request.h +++ b/scene/main/http_request.h @@ -32,6 +32,7 @@ #include "node.h" #include "io/http_client.h" #include "os/file_access.h" +#include "os/thread.h" class HTTPRequest : public Node { @@ -69,7 +70,7 @@ private: bool request_sent; Ref<HTTPClient> client; ByteArray body; - bool use_threads; + volatile bool use_threads; bool got_response; int response_code; @@ -80,7 +81,7 @@ private: FileAccess *file; int body_len; - int downloaded; + volatile int downloaded; int body_size_limit; int redirections; @@ -93,11 +94,19 @@ private: void _redirect_request(const String& p_new_url); + bool _handle_response(bool *ret_value); Error _parse_url(const String& p_url); Error _request(); + volatile bool thread_done; + volatile bool thread_request_quit; + + Thread *thread; + + void _request_done(int p_status, int p_code, const StringArray& headers, const ByteArray& p_data); + static void _thread_func(void *p_userdata); protected: diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index bdb2754e5e..f182f2c96c 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1782,7 +1782,6 @@ void Viewport::_gui_input_event(InputEvent p_event) { if (top->data.modal_exclusive || top->data.modal_frame==OS::get_singleton()->get_frames_drawn()) { //cancel event, sorry, modal exclusive EATS UP ALL //alternative, you can't pop out a window the same frame it was made modal (fixes many issues) - //get_tree()->call_group(SceneTree::GROUP_CALL_REALTIME,"windows","_cancel_input_ID",p_event.ID); get_tree()->set_input_as_handled(); return; // no one gets the event if exclusive NO ONE } @@ -2034,8 +2033,22 @@ void Viewport::_gui_input_event(InputEvent p_event) { } + bool is_tooltip_shown = false; - if (can_tooltip) { + if (gui.tooltip_popup) { + if (can_tooltip) { + String tooltip = over->get_tooltip(gui.tooltip->get_global_transform().xform_inv(mpos)); + + if (tooltip.length() == 0) + _gui_cancel_tooltip(); + else if (tooltip == gui.tooltip_label->get_text()) + is_tooltip_shown = true; + } + else + _gui_cancel_tooltip(); + } + + if (can_tooltip && !is_tooltip_shown) { gui.tooltip=over; gui.tooltip_pos=mpos;//(parent_xform * get_transform()).affine_inverse().xform(pos); @@ -2062,7 +2075,6 @@ void Viewport::_gui_input_event(InputEvent p_event) { - //get_tree()->call_group(SceneTree::GROUP_CALL_REALTIME,"windows","_cancel_input_ID",p_event.ID); get_tree()->set_input_as_handled(); @@ -2102,7 +2114,7 @@ void Viewport::_gui_input_event(InputEvent p_event) { if (gui.key_event_accepted) { - get_tree()->call_group(SceneTree::GROUP_CALL_REALTIME,"windows","_cancel_input_ID",p_event.ID); + get_tree()->set_input_as_handled(); break; } } @@ -2162,7 +2174,7 @@ void Viewport::_gui_input_event(InputEvent p_event) { if (next) { next->grab_focus(); - get_tree()->call_group(SceneTree::GROUP_CALL_REALTIME,"windows","_cancel_input_ID",p_event.ID); + get_tree()->set_input_as_handled(); } } @@ -2355,8 +2367,7 @@ void Viewport::_gui_control_grab_focus(Control* p_control) { if (gui.key_focus && gui.key_focus==p_control) return; - _gui_remove_focus(); - get_tree()->call_group(SceneTree::GROUP_CALL_REALTIME,"windows","_gui_remove_focus"); + get_tree()->call_group(SceneTree::GROUP_CALL_REALTIME,"_viewports","_gui_remove_focus"); gui.key_focus=p_control; p_control->notification(Control::NOTIFICATION_FOCUS_ENTER); p_control->update(); @@ -2664,6 +2675,7 @@ void Viewport::_bind_methods() { ObjectTypeDB::bind_method(_MD("is_input_disabled"), &Viewport::is_input_disabled); ObjectTypeDB::bind_method(_MD("_gui_show_tooltip"), &Viewport::_gui_show_tooltip); + ObjectTypeDB::bind_method(_MD("_gui_remove_focus"), &Viewport::_gui_remove_focus); ADD_PROPERTY( PropertyInfo(Variant::RECT2,"rect"), _SCS("set_rect"), _SCS("get_rect") ); ADD_PROPERTY( PropertyInfo(Variant::BOOL,"own_world"), _SCS("set_use_own_world"), _SCS("is_using_own_world") ); diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index 88ff09e961..29460790ff 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -644,7 +644,7 @@ Vector2 Curve2D::interpolate_baked(float p_offset,bool p_cubic) const{ if (p_offset>=baked_max_ofs) return r[bpc-1]; - int idx = Math::floor(p_offset/bake_interval); + int idx = Math::floor((double)p_offset/(double)bake_interval); float frac = Math::fmod(p_offset,bake_interval); if (idx>=bpc-1) { @@ -1117,7 +1117,7 @@ Vector3 Curve3D::interpolate_baked(float p_offset,bool p_cubic) const{ if (p_offset>=baked_max_ofs) return r[bpc-1]; - int idx = Math::floor(p_offset/bake_interval); + int idx = Math::floor((double)p_offset/(double)bake_interval); float frac = Math::fmod(p_offset,bake_interval); if (idx>=bpc-1) { @@ -1161,7 +1161,7 @@ float Curve3D::interpolate_baked_tilt(float p_offset) const{ if (p_offset>=baked_max_ofs) return r[bpc-1]; - int idx = Math::floor(p_offset/bake_interval); + int idx = Math::floor((double)p_offset/(double)bake_interval); float frac = Math::fmod(p_offset,bake_interval); if (idx>=bpc-1) { diff --git a/servers/physics/body_pair_sw.cpp b/servers/physics/body_pair_sw.cpp index 3202e52abb..43e52f26e8 100644 --- a/servers/physics/body_pair_sw.cpp +++ b/servers/physics/body_pair_sw.cpp @@ -323,13 +323,13 @@ bool BodyPairSW::setup(float p_step) { #endif if (A->can_report_contacts()) { - Vector3 crB = A->get_angular_velocity().cross( c.rA ) + A->get_linear_velocity(); - A->add_contact(global_A,-c.normal,depth,shape_A,global_B,shape_B,B->get_instance_id(),B->get_self(),crB); + Vector3 crA = A->get_angular_velocity().cross( c.rA ) + A->get_linear_velocity(); + A->add_contact(global_A,-c.normal,depth,shape_A,global_B,shape_B,B->get_instance_id(),B->get_self(),crA); } if (B->can_report_contacts()) { - Vector3 crA = A->get_angular_velocity().cross( c.rB ) + A->get_linear_velocity(); - B->add_contact(global_B,c.normal,depth,shape_B,global_A,shape_A,A->get_instance_id(),A->get_self(),crA); + Vector3 crB = B->get_angular_velocity().cross( c.rB ) + B->get_linear_velocity(); + B->add_contact(global_B,c.normal,depth,shape_B,global_A,shape_A,A->get_instance_id(),A->get_self(),crB); } if (A->is_shape_set_as_trigger(shape_A) || B->is_shape_set_as_trigger(shape_B) || (A->get_mode()<=PhysicsServer::BODY_MODE_KINEMATIC && B->get_mode()<=PhysicsServer::BODY_MODE_KINEMATIC)) { diff --git a/tools/editor/asset_library_editor_plugin.cpp b/tools/editor/asset_library_editor_plugin.cpp index 4f685badfb..3fd5d2b5d1 100644 --- a/tools/editor/asset_library_editor_plugin.cpp +++ b/tools/editor/asset_library_editor_plugin.cpp @@ -134,13 +134,13 @@ EditorAssetLibraryItem::EditorAssetLibraryItem() { category = memnew( LinkButton ); category->set_text("Editor Tools"); category->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER); - title->connect("pressed",this,"_category_clicked"); + category->connect("pressed",this,"_category_clicked"); vb->add_child(category); author = memnew( LinkButton ); author->set_text("Johny Tolengo"); author->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER); - title->connect("pressed",this,"_author_clicked"); + author->connect("pressed",this,"_author_clicked"); vb->add_child(author); HBoxContainer *rating_hb = memnew( HBoxContainer ); @@ -325,6 +325,7 @@ void EditorAssetLibraryItemDownload::_http_download_completed(int p_status, int String error_text; + print_line("COMPLETED: "+itos(p_status)+" code: "+itos(p_code)+" data size: "+itos(p_data.size())); switch(p_status) { @@ -383,7 +384,9 @@ void EditorAssetLibraryItemDownload::_http_download_completed(int p_status, int print_line("max: "+itos(download->get_body_size())+" bytes: "+itos(download->get_downloaded_bytes())); install->set_disabled(false); - status->set_text("Success!"); + progress->set_val(download->get_downloaded_bytes()); + + status->set_text("Success! ("+String::humanize_size(download->get_downloaded_bytes())+")"); set_process(false); } @@ -411,6 +414,10 @@ void EditorAssetLibraryItemDownload::_notification(int p_what) { progress->set_val(download->get_downloaded_bytes()); int cstatus = download->get_http_client_status(); + + if (cstatus==HTTPClient::STATUS_BODY) + status->set_text("Fetching: "+String::humanize_size(download->get_downloaded_bytes())); + if (cstatus!=prev_status) { switch(cstatus) { @@ -423,9 +430,6 @@ void EditorAssetLibraryItemDownload::_notification(int p_what) { case HTTPClient::STATUS_REQUESTING: { status->set_text("Requesting.."); } break; - case HTTPClient::STATUS_BODY: { - status->set_text("Downloading.."); - } break; default: {} } prev_status=cstatus; @@ -457,6 +461,7 @@ void EditorAssetLibraryItemDownload::_install() { void EditorAssetLibraryItemDownload::_make_request() { download->cancel_request(); download->set_download_file(EditorSettings::get_singleton()->get_settings_path().plus_file("tmp").plus_file("tmp_asset_"+itos(asset_id))+".zip"); + Error err = download->request(host); if(err!=OK) { status->set_text("Error making request"); @@ -1007,6 +1012,7 @@ void EditorAssetLibrary::_api_request(const String& p_request, RequestType p_req if (requesting!=REQUESTING_NONE) { request->cancel_request(); } + requesting=p_request_type; error_hb->hide(); @@ -1465,6 +1471,7 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) { request = memnew( HTTPRequest ); add_child(request); + request->set_use_threads(EDITOR_DEF("asset_library/use_threads",true)); request->connect("request_completed",this,"_http_request_completed"); last_queue_id=0; diff --git a/tools/editor/code_editor.cpp b/tools/editor/code_editor.cpp index 6c8d25aa01..ed7a46d70d 100644 --- a/tools/editor/code_editor.cpp +++ b/tools/editor/code_editor.cpp @@ -1032,8 +1032,8 @@ void CodeTextEditor::_reset_zoom() { void CodeTextEditor::_line_col_changed() { - String text = String()+TTR("Line:")+" "+itos(text_editor->cursor_get_line()+1)+", "+TTR("Col:")+" "+itos(text_editor->cursor_get_column()); - line_col->set_text(text); + line_nb->set_text(itos(text_editor->cursor_get_line() + 1)); + col_nb->set_text(itos(text_editor->cursor_get_column())); } void CodeTextEditor::_text_changed() { @@ -1191,6 +1191,7 @@ CodeTextEditor::CodeTextEditor() { HBoxContainer *status_bar = memnew( HBoxContainer ); status_mc->add_child(status_bar); status_bar->set_h_size_flags(SIZE_EXPAND_FILL); + status_bar->add_child( memnew( Label ) ); //to keep the height if the other labels are not visible idle = memnew( Timer ); add_child(idle); @@ -1212,14 +1213,33 @@ CodeTextEditor::CodeTextEditor() { status_bar->add_spacer(); - line_col = memnew( Label ); - status_bar->add_child(line_col); - line_col->set_valign(Label::VALIGN_CENTER); - line_col->set_autowrap(true); - line_col->set_v_size_flags(SIZE_FILL); - line_col->set_custom_minimum_size(Size2(100,1)*EDSCALE); - status_bar->add_child( memnew( Label ) ); //to keep the height if the other labels are not visible - + Label *line_txt = memnew( Label ); + status_bar->add_child(line_txt); + line_txt->set_align(Label::ALIGN_RIGHT); + line_txt->set_valign(Label::VALIGN_CENTER); + line_txt->set_v_size_flags(SIZE_FILL); + line_txt->set_text(TTR("Line:")); + + line_nb = memnew( Label ); + status_bar->add_child(line_nb); + line_nb->set_valign(Label::VALIGN_CENTER); + line_nb->set_v_size_flags(SIZE_FILL); + line_nb->set_autowrap(true); // workaround to prevent resizing the label on each change + line_nb->set_custom_minimum_size(Size2(40,1)*EDSCALE); + + Label *col_txt = memnew( Label ); + status_bar->add_child(col_txt); + col_txt->set_align(Label::ALIGN_RIGHT); + col_txt->set_valign(Label::VALIGN_CENTER); + col_txt->set_v_size_flags(SIZE_FILL); + col_txt->set_text(TTR("Col:")); + + col_nb = memnew( Label ); + status_bar->add_child(col_nb); + col_nb->set_valign(Label::VALIGN_CENTER); + col_nb->set_v_size_flags(SIZE_FILL); + col_nb->set_autowrap(true); // workaround to prevent resizing the label on each change + col_nb->set_custom_minimum_size(Size2(40,1)*EDSCALE); text_editor->connect("input_event", this,"_text_editor_input_event"); text_editor->connect("cursor_changed", this,"_line_col_changed"); diff --git a/tools/editor/code_editor.h b/tools/editor/code_editor.h index 7983c46f7a..88d882bd33 100644 --- a/tools/editor/code_editor.h +++ b/tools/editor/code_editor.h @@ -196,7 +196,8 @@ class CodeTextEditor : public VBoxContainer { TextEdit *text_editor; FindReplaceBar *find_replace_bar; - Label *line_col; + Label *line_nb; + Label *col_nb; Label *info; Timer *idle; Timer *code_complete_timer; diff --git a/tools/editor/editor_plugin.cpp b/tools/editor/editor_plugin.cpp index 01e6b613c0..0d162cbe56 100644 --- a/tools/editor/editor_plugin.cpp +++ b/tools/editor/editor_plugin.cpp @@ -44,9 +44,9 @@ void EditorPlugin::remove_custom_type(const String& p_type){ } -void EditorPlugin::add_control_to_bottom_panel(Control *p_control, const String &p_title) { +ToolButton * EditorPlugin::add_control_to_bottom_panel(Control *p_control, const String &p_title) { - EditorNode::get_singleton()->add_bottom_panel_item(p_title,p_control); + return EditorNode::get_singleton()->add_bottom_panel_item(p_title,p_control); } void EditorPlugin::add_control_to_dock(DockSlot p_slot,Control *p_control) { @@ -284,7 +284,7 @@ Control *EditorPlugin::get_base_control() { void EditorPlugin::_bind_methods() { ObjectTypeDB::bind_method(_MD("add_control_to_container","container","control:Control"),&EditorPlugin::add_control_to_container); - ObjectTypeDB::bind_method(_MD("add_control_to_bottom_panel","control:Control","title"),&EditorPlugin::add_control_to_bottom_panel); + ObjectTypeDB::bind_method(_MD("add_control_to_bottom_panel:ToolButton","control:Control","title"),&EditorPlugin::add_control_to_bottom_panel); ObjectTypeDB::bind_method(_MD("add_control_to_dock","slot","control:Control"),&EditorPlugin::add_control_to_dock); ObjectTypeDB::bind_method(_MD("remove_control_from_docks","control:Control"),&EditorPlugin::remove_control_from_docks); ObjectTypeDB::bind_method(_MD("remove_control_from_bottom_panel","control:Control"),&EditorPlugin::remove_control_from_bottom_panel); diff --git a/tools/editor/editor_plugin.h b/tools/editor/editor_plugin.h index b93b6624d0..b960a7d5af 100644 --- a/tools/editor/editor_plugin.h +++ b/tools/editor/editor_plugin.h @@ -29,6 +29,7 @@ #ifndef EDITOR_PLUGIN_H #define EDITOR_PLUGIN_H +#include "scene/gui/tool_button.h" #include "scene/main/node.h" #include "scene/resources/texture.h" #include "undo_redo.h" @@ -92,7 +93,7 @@ public: //TODO: send a resoucre for editing to the editor node? void add_control_to_container(CustomControlContainer p_location, Control *p_control); - void add_control_to_bottom_panel(Control *p_control, const String &p_title); + ToolButton *add_control_to_bottom_panel(Control *p_control, const String &p_title); void add_control_to_dock(DockSlot p_slot,Control *p_control); void remove_control_from_docks(Control *p_control); void remove_control_from_bottom_panel(Control *p_control); diff --git a/version.py b/version.py index dd80ca1b19..19ff05f715 100644 --- a/version.py +++ b/version.py @@ -2,4 +2,4 @@ short_name="godot" name="Godot Engine" major=2 minor=1 -status="beta" +status="rc1" |