mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-01-24 16:06:51 +08:00
v1.7.11
This commit is contained in:
parent
25c3f2ee01
commit
645e1b3eac
@ -4001,52 +4001,52 @@
|
||||
"assets/build/workspace/onslaughtplug.py": "https://files.ballistica.net/cache/ba1/08/ed/d671c39a3ece6362a6d985112c8e",
|
||||
"assets/build/workspace/runaroundplug.py": "https://files.ballistica.net/cache/ba1/4d/71/1292911f5369bdb83ef6a34921c0",
|
||||
"assets/src/ba_data/python/ba/_generated/__init__.py": "https://files.ballistica.net/cache/ba1/ee/e8/cad05aa531c7faf7ff7b96db7f6e",
|
||||
"assets/src/ba_data/python/ba/_generated/enums.py": "https://files.ballistica.net/cache/ba1/b2/e5/0ee0561e16257a32830645239f34",
|
||||
"assets/src/ba_data/python/ba/_generated/enums.py": "https://files.ballistica.net/cache/ba1/1c/77/ac670a5118abdf8a7687af0e159b",
|
||||
"ballisticacore-windows/Generic/BallisticaCore.ico": "https://files.ballistica.net/cache/ba1/89/c0/e32c7d2a35dc9aef57cc73b0911a",
|
||||
"build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/c0/b2/ff130457d068b900d961e2354adc",
|
||||
"build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/3e/6f/e5eb8e0646c8a56d81a744b83033",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/bc/a0/8801adcaae0ebf8e065e50df88e5",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/60/0d/a08147b59d213ef1a61b6a393997",
|
||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/c0/16/798fbe8f0e563c2a1158f612df18",
|
||||
"build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/b0/f6/6db11e5da130e85b59c1e99f34d2",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/c1/95/30cb68910669c012bf5f3e0f5d8d",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/9d/53/124cd00020647d0ea73114368d9f",
|
||||
"build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/e4/a0/14debf6938db7d2db3e6a66dd51a",
|
||||
"build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/af/4d/9d01c0b54f17152439ff229585a8",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/d3/ef/35e69f1dc7e0d2ec40814169ee16",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/46/77/675fc277cb7d0b4362b4ad002dbd",
|
||||
"build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/75/de/899f4b4c7864a775a918735d2050",
|
||||
"build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/ac/31/75bab23c1617aaf9c07c428ee477",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/18/0c/ff933dc5901512c7bcd8fc9d0c2a",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/5d/ad/992a5b10cfd2fb13ce402d64e672",
|
||||
"build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/3f/3e/d7ffee3c48c5e7e61432274c303c",
|
||||
"build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/d2/aa/ac8b35f42058ce4accd33f689366",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/f7/d9/037846d00c1491e4d09609cbbec6",
|
||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/ae/47/d79b5b8c9168944641514072c56c",
|
||||
"build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a0/2a/df32b0c34525af7de212e9a2cf30",
|
||||
"build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/8f/28/f103463730f785ba4fe6a91f8943",
|
||||
"build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/29/4a/0df587009671edf52d6939c8f966",
|
||||
"build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/c8/df/12ea6b0a703568e52a84001ca2db",
|
||||
"build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/fb/29/5a74e41f2aa46aaa4ebb62b2612f",
|
||||
"build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d0/89/1995ef2194458c78c546698de761",
|
||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7c/7e/01b2fbad142edaac0c0af7853874",
|
||||
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/9c/2f/3d0f4b2439b7da9def68597c14c8",
|
||||
"build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/26/d8/2a352f23af0375e97752da915d38",
|
||||
"build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a6/0f/72032ea268956736a6eabce738c3",
|
||||
"build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/92/5d/a8e2b0137a4e67821fc0c233704f",
|
||||
"build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/01/7c/c21ceaa90f0a0794979056c0a5dc",
|
||||
"build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/60/4a/238ad7b1d8b5cac2376e58cdd6de",
|
||||
"build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/27/b6/676cee2d1996ab592e1006ba4721",
|
||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b1/fe/bf79be22fc78040b28b60aa2a8e3",
|
||||
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/37/3e/689be0ffce83ac46775c5dec6446",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/ff/e0/707a7f27737a95cd484a8105cb1e",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/22/40/9c60cea175b1685b6ceb49825753",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/b7/2a/6bf91a3cce672678d4313535e5f8",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/31/97/77ee5fbf99340b8a5368b691fffb",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/de/9c/7232b3307214ae9b18c7c7673afa",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/ec/e8/a98b515b312ab875ee58464050e8",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/21/f0/41a934523b6b34ba5f9e9cee96c6",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/43/97/158153da90bd41bba18cd245a6be",
|
||||
"build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/e8/7b/1dbe9dbf1819b08421362d3203cc",
|
||||
"build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/8a/16/4ab8922ba3524473860b9e3c3fd0",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ee/55/a23141dd32ae2849664267fc24b3",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/af/c7/5e8d3e6873382d7f9b2e6ecb8273",
|
||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/2e/8e/b4561b47b90255ac660612516890",
|
||||
"build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/05/c0/dd90b84f7fe0a31fdaebd4e5532e",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/3d/29/52ca62a01b5df84af8c2578646d2",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/88/17/f7891bf7ba6158598260160df7ba",
|
||||
"build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/13/7c/ec7e0bebd3608bde2dc6434cc3d1",
|
||||
"build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/d3/16/ccefa4342be01801c1e152f76aee",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ef/45/e1f2aa107b62245c23ae613022fe",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/aa/d7/65771c73cea67f0549c1a5f69b0b",
|
||||
"build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/56/a2/f0ad7ca2cd6a99c33dab76b6f90a",
|
||||
"build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/48/a6/dbe19639b711ff8c5030eec054b4",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/0f/c8/b332d9bc4dd72347613a0a50aacf",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/a3/9b/26a032924048ae2a2d94eec53c78",
|
||||
"build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/b4/04/7be7602bcb91eee479402875feb7",
|
||||
"build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/52/ba/0aba5f9247a0e7af5738d6487df3",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/42/45/4cc46885f0d29c62b4b08be440c6",
|
||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/4d/a0/37cfb3c48f7a7026638d68e07855",
|
||||
"build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/fc/d7/66e38e026edcd53ab470c74d7224",
|
||||
"build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/9a/42/7ddd583f9c874837a4a7f902953f",
|
||||
"build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/22/4a/7771e45d6f8316d4c738d186062e",
|
||||
"build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/44/6b/09b134e2f9b8bd276ff6351b74ee",
|
||||
"build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/4e/d2/43048339ea0b88895e0e6c206f20",
|
||||
"build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/78/cd/021300c2cacc307bdda1763d9845",
|
||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/f4/b2/9d68ec3b7c0981efed766e0dfdd7",
|
||||
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/1a/f2/4bbf3362f5735492e1bb27606013",
|
||||
"build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e6/df/d9e68d3b7b1593a3400b1d2b14c7",
|
||||
"build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e0/e0/58c6709938ed40089213be8c7e37",
|
||||
"build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/29/d9/2c1234aaccc1c0d50962d7d61f9f",
|
||||
"build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/5d/ae/45084efc0b90744bcfed29112223",
|
||||
"build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/62/92/0bd650555ce0042a9c56c4a0751f",
|
||||
"build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/be/8b/c9423cd7566404e2e74d0db50506",
|
||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/cf/15/c8ce7acd9c5a973d6886771c099e",
|
||||
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/fa/59/26dc8860ecd9644b725e9428a312",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/91/39/6ff63efde3ae2c98474712f78b56",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/18/f6/bdf379aadd643ac3ae46094d8545",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/91/c3/9fe6f6916de5e8086f3e1c9c0c1f",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/41/c0/730d363fbccc0639ac24f7318d32",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/18/82/af34d2eececb1b43018d3016f54c",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/86/2f/a7abcfde5205ba2fdbf844decffb",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/a7/ab/854144e721101e2eb404ff0a6d51",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/f4/92/e786733e776c2b63e9f5340f88d8",
|
||||
"src/ballistica/generated/python_embedded/binding.inc": "https://files.ballistica.net/cache/ba1/c0/32/b7907e3859a5c5013a3d97b6b523",
|
||||
"src/ballistica/generated/python_embedded/bootstrap.inc": "https://files.ballistica.net/cache/ba1/2d/4f/f4fe67827f36cd59cd5193333a02",
|
||||
"src/ballistica/generated/python_embedded/bootstrap_monolithic.inc": "https://files.ballistica.net/cache/ba1/ef/c1/aa5f1aa10af89f5c0b1e616355fd"
|
||||
|
||||
8
.idea/dictionaries/ericf.xml
generated
8
.idea/dictionaries/ericf.xml
generated
@ -1063,6 +1063,7 @@
|
||||
<w>getopt</w>
|
||||
<w>getplayer</w>
|
||||
<w>getpt</w>
|
||||
<w>getr</w>
|
||||
<w>getrefs</w>
|
||||
<w>getremote</w>
|
||||
<w>getres</w>
|
||||
@ -1738,6 +1739,7 @@
|
||||
<w>objid</w>
|
||||
<w>objname</w>
|
||||
<w>objs</w>
|
||||
<w>objsizes</w>
|
||||
<w>objt</w>
|
||||
<w>objtoyaml</w>
|
||||
<w>objtype</w>
|
||||
@ -1851,6 +1853,7 @@
|
||||
<w>pdoc</w>
|
||||
<w>pedit</w>
|
||||
<w>peditui</w>
|
||||
<w>peerinfo</w>
|
||||
<w>peername</w>
|
||||
<w>pentry</w>
|
||||
<w>perma</w>
|
||||
@ -1972,6 +1975,7 @@
|
||||
<w>printobjects</w>
|
||||
<w>printpaths</w>
|
||||
<w>printrefs</w>
|
||||
<w>printsizes</w>
|
||||
<w>printtypes</w>
|
||||
<w>priv</w>
|
||||
<w>privatetab</w>
|
||||
@ -2335,6 +2339,8 @@
|
||||
<w>sitebuiltins</w>
|
||||
<w>skey</w>
|
||||
<w>sline</w>
|
||||
<w>slist</w>
|
||||
<w>slists</w>
|
||||
<w>slval</w>
|
||||
<w>smag</w>
|
||||
<w>smallscale</w>
|
||||
@ -2646,6 +2652,8 @@
|
||||
<w>toplevel</w>
|
||||
<w>toplevelfiles</w>
|
||||
<w>totaldudes</w>
|
||||
<w>totalobjmb</w>
|
||||
<w>totalobjsize</w>
|
||||
<w>totalpts</w>
|
||||
<w>totaltime</w>
|
||||
<w>totalwaves</w>
|
||||
|
||||
@ -1,5 +1,11 @@
|
||||
### 1.7.11 (build 20899, api 7, 2022-10-10)
|
||||
### 1.7.11 (build 20909, api 7, 2022-10-15)
|
||||
- Switched our Python autoformatting from yapf to black. The yapf project seems to be mostly dead whereas black seems to be thriving. The final straw was yapf not supporting the `match` statement in Python 3.10.
|
||||
- Added `has_settings_ui()` and `show_settings_ui()` methods to ba.Plugin. Plugins can use these to enable a 'Settings' button next to them in the plugin manager that brings up a custom UI.
|
||||
- Fixed workspaces functionality, which I broke rather terribly in 1.7.10 when I forgot to test it against all the internal changes there (sorry). Note that there is a slight downside to having workspace syncing enabled now in that it turns off the fast-v2-relaunch-login optimization from 1.7.10.
|
||||
- App should now show a message when workspace has been changed and a restart is needed for it to take effect.
|
||||
- Fixed an issue where `ba.open_url()` would fall back to internal url display window on some newer Android versions instead of opening a browser. It should now correctly open a browser on regular Android. On AndroidTV/iiRcade/VR it will now always display the internal pop-up. It was trying to use fancy logic before to determine if a browser was available but this seemed to be flaky. Holler if this is not working well on your device/situation.
|
||||
- The internal 'fallback' `ba.open_url()` window which shows a url string when a system browser is not available now has a qrcode and a copy button (where copy/paste is supported).
|
||||
- Added a 'force_internal' arg to `ba.open_url()` if you would like to always use the internal window instead of attempting to open a browser. Now that we show a copy button and qr code there are some cases where this may be desirable.
|
||||
|
||||
### 1.7.10 (build 20895, api 7, 2022-10-09)
|
||||
- Added eval support for cloud-console. This means you can type something like '1+1' in the console and see '2' printed. This is how Python behaves in the stdin console or in-game console or the standard Python interpreter.
|
||||
|
||||
@ -1 +1 @@
|
||||
180157672676216986210895241045962795292
|
||||
31242320059036633417109806113241486230
|
||||
@ -2609,14 +2609,15 @@ def open_file_externally(path: str) -> None:
|
||||
return None
|
||||
|
||||
|
||||
def open_url(address: str) -> None:
|
||||
def open_url(address: str, force_internal: bool = False) -> None:
|
||||
|
||||
"""Open a provided URL.
|
||||
|
||||
Category: **General Utility Functions**
|
||||
|
||||
Open the provided url in a web-browser, or display the URL
|
||||
string in a window if that isn't possible.
|
||||
string in a window if that isn't possible (or if force_internal
|
||||
is True).
|
||||
"""
|
||||
return None
|
||||
|
||||
|
||||
@ -64,6 +64,7 @@ from _ba import (
|
||||
getdata,
|
||||
in_logic_thread,
|
||||
)
|
||||
from ba._accountv2 import AccountV2Handle
|
||||
from ba._activity import Activity
|
||||
from ba._plugin import PotentialPlugin, Plugin, PluginSubsystem
|
||||
from ba._actor import Actor
|
||||
@ -187,6 +188,7 @@ from ba._collision import Collision, getcollision
|
||||
app: App
|
||||
|
||||
__all__ = [
|
||||
'AccountV2Handle',
|
||||
'Achievement',
|
||||
'AchievementSubsystem',
|
||||
'Activity',
|
||||
|
||||
@ -77,6 +77,7 @@ class AccountV2Subsystem:
|
||||
):
|
||||
self._kicked_off_workspace_load = True
|
||||
_ba.app.workspaces.set_active_workspace(
|
||||
account=account,
|
||||
workspaceid=account.workspaceid,
|
||||
workspacename=account.workspacename,
|
||||
on_completed=self._on_set_active_workspace_completed,
|
||||
|
||||
@ -47,7 +47,7 @@ def bootstrap() -> None:
|
||||
|
||||
# Give a soft warning if we're being used with a different binary
|
||||
# version than we expect.
|
||||
expected_build = 20899
|
||||
expected_build = 20909
|
||||
running_build: int = env['build_number']
|
||||
if running_build != expected_build:
|
||||
print(
|
||||
|
||||
@ -222,3 +222,10 @@ class Plugin:
|
||||
|
||||
def on_app_shutdown(self) -> None:
|
||||
"""Called before closing the application."""
|
||||
|
||||
def has_settings_ui(self) -> bool:
|
||||
"""Called to ask if we have settings UI we can show."""
|
||||
return False
|
||||
|
||||
def show_settings_ui(self, source_widget: ba.Widget | None) -> None:
|
||||
"""Called to show our settings UI."""
|
||||
|
||||
@ -36,6 +36,7 @@ class WorkspaceSubsystem:
|
||||
|
||||
def set_active_workspace(
|
||||
self,
|
||||
account: ba.AccountV2Handle,
|
||||
workspaceid: str,
|
||||
workspacename: str,
|
||||
on_completed: Callable[[], None],
|
||||
@ -46,6 +47,7 @@ class WorkspaceSubsystem:
|
||||
# interactivity.
|
||||
Thread(
|
||||
target=lambda: self._set_active_workspace_bg(
|
||||
account=account,
|
||||
workspaceid=workspaceid,
|
||||
workspacename=workspacename,
|
||||
on_completed=on_completed,
|
||||
@ -63,6 +65,7 @@ class WorkspaceSubsystem:
|
||||
|
||||
def _set_active_workspace_bg(
|
||||
self,
|
||||
account: ba.AccountV2Handle,
|
||||
workspaceid: str,
|
||||
workspacename: str,
|
||||
on_completed: Callable[[], None],
|
||||
@ -91,11 +94,12 @@ class WorkspaceSubsystem:
|
||||
state = bacommon.cloud.WorkspaceFetchState(manifest=manifest)
|
||||
|
||||
while True:
|
||||
response = _ba.app.cloud.send_message(
|
||||
bacommon.cloud.WorkspaceFetchMessage(
|
||||
workspaceid=workspaceid, state=state
|
||||
with account:
|
||||
response = _ba.app.cloud.send_message(
|
||||
bacommon.cloud.WorkspaceFetchMessage(
|
||||
workspaceid=workspaceid, state=state
|
||||
)
|
||||
)
|
||||
)
|
||||
state = response.state
|
||||
self._handle_deletes(
|
||||
workspace_dir=wspath, deletes=response.deletes
|
||||
|
||||
@ -21,6 +21,7 @@ class PluginSettingsWindow(ba.Window):
|
||||
origin_widget: ba.Widget | None = None,
|
||||
):
|
||||
# pylint: disable=too-many-locals
|
||||
# pylint: disable=too-many-statements
|
||||
app = ba.app
|
||||
|
||||
# If they provided an origin-widget, scale up from that.
|
||||
@ -113,11 +114,9 @@ class PluginSettingsWindow(ba.Window):
|
||||
highlight=False,
|
||||
size=(self._scroll_width, self._scroll_height),
|
||||
selection_loops_to_parent=True,
|
||||
claims_left_right=True,
|
||||
)
|
||||
ba.widget(edit=self._scrollwidget, right_widget=self._scrollwidget)
|
||||
self._subcontainer = ba.columnwidget(
|
||||
parent=self._scrollwidget, selection_loops_to_parent=True
|
||||
)
|
||||
|
||||
if ba.app.meta.scanresults is None:
|
||||
ba.screenmessage(
|
||||
@ -127,17 +126,32 @@ class PluginSettingsWindow(ba.Window):
|
||||
pluglist = ba.app.plugins.potential_plugins
|
||||
plugstates: dict[str, dict] = ba.app.config.setdefault('Plugins', {})
|
||||
assert isinstance(plugstates, dict)
|
||||
|
||||
plug_line_height = 50
|
||||
sub_width = self._scroll_width
|
||||
sub_height = len(pluglist) * plug_line_height
|
||||
self._subcontainer = ba.containerwidget(
|
||||
parent=self._scrollwidget,
|
||||
size=(sub_width, sub_height),
|
||||
background=False,
|
||||
)
|
||||
|
||||
for i, availplug in enumerate(pluglist):
|
||||
active = availplug.class_path in ba.app.plugins.active_plugins
|
||||
plugin = ba.app.plugins.active_plugins.get(availplug.class_path)
|
||||
active = plugin is not None
|
||||
|
||||
plugstate = plugstates.setdefault(availplug.class_path, {})
|
||||
checked = plugstate.get('enabled', False)
|
||||
assert isinstance(checked, bool)
|
||||
|
||||
item_y = sub_height - (i + 1) * plug_line_height
|
||||
check = ba.checkboxwidget(
|
||||
parent=self._subcontainer,
|
||||
text=availplug.display_name,
|
||||
autoselect=True,
|
||||
value=checked,
|
||||
maxwidth=self._scroll_width - 100,
|
||||
maxwidth=self._scroll_width - 200,
|
||||
position=(10, item_y),
|
||||
size=(self._scroll_width - 40, 50),
|
||||
on_value_change_call=ba.Call(
|
||||
self._check_value_changed, availplug
|
||||
@ -150,14 +164,35 @@ class PluginSettingsWindow(ba.Window):
|
||||
else (0.6, 0.6, 0.6)
|
||||
),
|
||||
)
|
||||
if plugin is not None and plugin.has_settings_ui():
|
||||
button = ba.buttonwidget(
|
||||
parent=self._subcontainer,
|
||||
label=ba.Lstr(resource='mainMenu.settingsText'),
|
||||
autoselect=True,
|
||||
size=(100, 40),
|
||||
position=(sub_width - 130, item_y + 6),
|
||||
)
|
||||
ba.buttonwidget(
|
||||
edit=button,
|
||||
on_activate_call=ba.Call(plugin.show_settings_ui, button),
|
||||
)
|
||||
else:
|
||||
button = None
|
||||
|
||||
# Allow getting back to back button.
|
||||
if i == 0:
|
||||
ba.widget(
|
||||
edit=check,
|
||||
up_widget=self._back_button,
|
||||
left_widget=self._back_button,
|
||||
)
|
||||
if button is not None:
|
||||
ba.widget(edit=button, up_widget=self._back_button)
|
||||
|
||||
# Make sure we scroll all the way to the end when using
|
||||
# keyboard/button nav.
|
||||
ba.widget(edit=check, show_buffer_top=40, show_buffer_bottom=40)
|
||||
|
||||
# Keep last from looping to back button when down is pressed.
|
||||
if i == len(pluglist) - 1:
|
||||
ba.widget(edit=check, down_widget=check)
|
||||
ba.containerwidget(
|
||||
edit=self._root_widget, selected_child=self._scrollwidget
|
||||
)
|
||||
|
||||
@ -17,52 +17,12 @@ class ShowURLWindow(ba.Window):
|
||||
# (for long URLs especially)
|
||||
app = ba.app
|
||||
uiscale = app.ui.uiscale
|
||||
if app.platform == 'android' and app.subplatform == 'alibaba':
|
||||
self._width = 500
|
||||
self._height = 500
|
||||
super().__init__(
|
||||
root_widget=ba.containerwidget(
|
||||
size=(self._width, self._height),
|
||||
transition='in_right',
|
||||
scale=(
|
||||
1.25
|
||||
if uiscale is ba.UIScale.SMALL
|
||||
else 1.25
|
||||
if uiscale is ba.UIScale.MEDIUM
|
||||
else 1.25
|
||||
),
|
||||
)
|
||||
)
|
||||
self._cancel_button = ba.buttonwidget(
|
||||
parent=self._root_widget,
|
||||
position=(50, self._height - 30),
|
||||
size=(50, 50),
|
||||
scale=0.6,
|
||||
label='',
|
||||
color=(0.6, 0.5, 0.6),
|
||||
on_activate_call=self._done,
|
||||
autoselect=True,
|
||||
icon=ba.gettexture('crossOut'),
|
||||
iconscale=1.2,
|
||||
)
|
||||
qr_size = 400
|
||||
ba.imagewidget(
|
||||
parent=self._root_widget,
|
||||
position=(
|
||||
self._width * 0.5 - qr_size * 0.5,
|
||||
self._height * 0.5 - qr_size * 0.5,
|
||||
),
|
||||
size=(qr_size, qr_size),
|
||||
texture=ba.internal.get_qrcode_texture(address),
|
||||
)
|
||||
ba.containerwidget(
|
||||
edit=self._root_widget, cancel_button=self._cancel_button
|
||||
)
|
||||
else:
|
||||
# show it as a simple string...
|
||||
self._width = 800
|
||||
self._height = 200
|
||||
self._root_widget = ba.containerwidget(
|
||||
self._address = address
|
||||
|
||||
self._width = 800
|
||||
self._height = 450
|
||||
super().__init__(
|
||||
root_widget=ba.containerwidget(
|
||||
size=(self._width, self._height + 40),
|
||||
transition='in_right',
|
||||
scale=(
|
||||
@ -73,43 +33,77 @@ class ShowURLWindow(ba.Window):
|
||||
else 1.25
|
||||
),
|
||||
)
|
||||
ba.textwidget(
|
||||
parent=self._root_widget,
|
||||
position=(self._width * 0.5, self._height - 10),
|
||||
size=(0, 0),
|
||||
color=ba.app.ui.title_color,
|
||||
h_align='center',
|
||||
v_align='center',
|
||||
text=ba.Lstr(resource='directBrowserToURLText'),
|
||||
maxwidth=self._width * 0.95,
|
||||
)
|
||||
ba.textwidget(
|
||||
parent=self._root_widget,
|
||||
position=(self._width * 0.5, self._height * 0.5 + 29),
|
||||
size=(0, 0),
|
||||
scale=1.3,
|
||||
color=ba.app.ui.infotextcolor,
|
||||
h_align='center',
|
||||
v_align='center',
|
||||
text=address,
|
||||
maxwidth=self._width * 0.95,
|
||||
)
|
||||
button_width = 200
|
||||
)
|
||||
ba.textwidget(
|
||||
parent=self._root_widget,
|
||||
position=(self._width * 0.5, self._height - 10),
|
||||
size=(0, 0),
|
||||
color=ba.app.ui.title_color,
|
||||
h_align='center',
|
||||
v_align='center',
|
||||
text=ba.Lstr(resource='directBrowserToURLText'),
|
||||
maxwidth=self._width * 0.95,
|
||||
)
|
||||
ba.textwidget(
|
||||
parent=self._root_widget,
|
||||
position=(self._width * 0.5, self._height - 60),
|
||||
size=(0, 0),
|
||||
scale=1.3,
|
||||
color=ba.app.ui.infotextcolor,
|
||||
h_align='center',
|
||||
v_align='center',
|
||||
text=address,
|
||||
maxwidth=self._width * 0.95,
|
||||
)
|
||||
button_width = 200
|
||||
|
||||
qr_size = 220
|
||||
ba.imagewidget(
|
||||
parent=self._root_widget,
|
||||
position=(
|
||||
self._width * 0.5 - qr_size * 0.5,
|
||||
self._height * 0.5 - qr_size * 0.5 + 10,
|
||||
),
|
||||
size=(qr_size, qr_size),
|
||||
texture=ba.internal.get_qrcode_texture(address),
|
||||
)
|
||||
|
||||
xoffs = 0
|
||||
if ba.clipboard_is_supported():
|
||||
xoffs = -150
|
||||
btn = ba.buttonwidget(
|
||||
parent=self._root_widget,
|
||||
position=(self._width * 0.5 - button_width * 0.5, 20),
|
||||
position=(
|
||||
self._width * 0.5 - button_width * 0.5 + xoffs,
|
||||
20,
|
||||
),
|
||||
size=(button_width, 65),
|
||||
label=ba.Lstr(resource='doneText'),
|
||||
on_activate_call=self._done,
|
||||
)
|
||||
# we have no 'cancel' button but still want to be able to
|
||||
# hit back/escape/etc to leave..
|
||||
ba.containerwidget(
|
||||
edit=self._root_widget,
|
||||
selected_child=btn,
|
||||
start_button=btn,
|
||||
on_cancel_call=btn.activate,
|
||||
autoselect=True,
|
||||
label=ba.Lstr(resource='copyText'),
|
||||
on_activate_call=self._copy,
|
||||
)
|
||||
xoffs = 150
|
||||
|
||||
btn = ba.buttonwidget(
|
||||
parent=self._root_widget,
|
||||
position=(self._width * 0.5 - button_width * 0.5 + xoffs, 20),
|
||||
size=(button_width, 65),
|
||||
autoselect=True,
|
||||
label=ba.Lstr(resource='doneText'),
|
||||
on_activate_call=self._done,
|
||||
)
|
||||
# we have no 'cancel' button but still want to be able to
|
||||
# hit back/escape/etc to leave..
|
||||
ba.containerwidget(
|
||||
edit=self._root_widget,
|
||||
selected_child=btn,
|
||||
start_button=btn,
|
||||
on_cancel_call=btn.activate,
|
||||
)
|
||||
|
||||
def _copy(self) -> None:
|
||||
ba.clipboard_set_text(self._address)
|
||||
ba.screenmessage(ba.Lstr(resource='copyConfirmText'), color=(0, 1, 0))
|
||||
|
||||
def _done(self) -> None:
|
||||
ba.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
|
||||
@ -567,6 +567,7 @@
|
||||
<w>getpublicpartyenabled</w>
|
||||
<w>getpublicpartymaxsize</w>
|
||||
<w>getqrcodetexture</w>
|
||||
<w>getr</w>
|
||||
<w>getrefs</w>
|
||||
<w>getres</w>
|
||||
<w>getsession</w>
|
||||
@ -918,6 +919,7 @@
|
||||
<w>objb</w>
|
||||
<w>objexists</w>
|
||||
<w>objid</w>
|
||||
<w>objsizes</w>
|
||||
<w>objtoyaml</w>
|
||||
<w>objtypes</w>
|
||||
<w>obstack</w>
|
||||
@ -988,6 +990,7 @@
|
||||
<w>pdataclass</w>
|
||||
<w>pdoc</w>
|
||||
<w>pdst</w>
|
||||
<w>peerinfo</w>
|
||||
<w>peername</w>
|
||||
<w>persp</w>
|
||||
<w>pflag</w>
|
||||
@ -1035,6 +1038,7 @@
|
||||
<w>printnodes</w>
|
||||
<w>printobjects</w>
|
||||
<w>printrefs</w>
|
||||
<w>printsizes</w>
|
||||
<w>printtypes</w>
|
||||
<w>priv</w>
|
||||
<w>privatetab</w>
|
||||
@ -1230,6 +1234,8 @@
|
||||
<w>simpletype</w>
|
||||
<w>sisssssssss</w>
|
||||
<w>sixteenbits</w>
|
||||
<w>slist</w>
|
||||
<w>slists</w>
|
||||
<w>smod</w>
|
||||
<w>smoothering</w>
|
||||
<w>smoothstep</w>
|
||||
@ -1369,6 +1375,8 @@
|
||||
<w>tmpmat</w>
|
||||
<w>tomer</w>
|
||||
<w>topos</w>
|
||||
<w>totalobjmb</w>
|
||||
<w>totalobjsize</w>
|
||||
<w>touchpad</w>
|
||||
<w>toucs</w>
|
||||
<w>tournamentbutton</w>
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
namespace ballistica {
|
||||
|
||||
// These are set automatically via script; don't modify them here.
|
||||
const int kAppBuildNumber = 20899;
|
||||
const int kAppBuildNumber = 20909;
|
||||
const char* kAppVersion = "1.7.11";
|
||||
|
||||
// Our standalone globals.
|
||||
|
||||
@ -2159,13 +2159,19 @@ auto PyBackPress(PyObject* self, PyObject* args, PyObject* keywds)
|
||||
auto PyOpenURL(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* {
|
||||
BA_PYTHON_TRY;
|
||||
const char* address = nullptr;
|
||||
static const char* kwlist[] = {"address", nullptr};
|
||||
if (!PyArg_ParseTupleAndKeywords(args, keywds, "s",
|
||||
const_cast<char**>(kwlist), &address)) {
|
||||
int force_internal{0};
|
||||
static const char* kwlist[] = {"address", "force_internal", nullptr};
|
||||
if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|p",
|
||||
const_cast<char**>(kwlist), &address,
|
||||
&force_internal)) {
|
||||
return nullptr;
|
||||
}
|
||||
assert(g_app_flavor);
|
||||
g_app_flavor->PushOpenURLCall(address);
|
||||
if (force_internal) {
|
||||
g_logic->PushShowURLCall(address);
|
||||
} else {
|
||||
g_app_flavor->PushOpenURLCall(address);
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
BA_PYTHON_CATCH;
|
||||
}
|
||||
@ -2271,14 +2277,15 @@ auto PythonMethodsUI::GetMethods() -> std::vector<PyMethodDef> {
|
||||
"Open the provided file in the default external app."},
|
||||
|
||||
{"open_url", (PyCFunction)PyOpenURL, METH_VARARGS | METH_KEYWORDS,
|
||||
"open_url(address: str) -> None\n"
|
||||
"open_url(address: str, force_internal: bool = False) -> None\n"
|
||||
"\n"
|
||||
"Open a provided URL.\n"
|
||||
"\n"
|
||||
"Category: **General Utility Functions**\n"
|
||||
"\n"
|
||||
"Open the provided url in a web-browser, or display the URL\n"
|
||||
"string in a window if that isn't possible.\n"},
|
||||
"string in a window if that isn't possible (or if force_internal\n"
|
||||
"is True).\n"},
|
||||
|
||||
{"back_press", (PyCFunction)PyBackPress, METH_VARARGS | METH_KEYWORDS,
|
||||
"back_press() -> None\n"
|
||||
|
||||
@ -432,7 +432,7 @@ def test_server_interrupt() -> None:
|
||||
await asyncio.sleep(0.2)
|
||||
tester.server.endpoint.close()
|
||||
|
||||
asyncio.create_task(_kill_connection())
|
||||
_task = asyncio.create_task(_kill_connection())
|
||||
with pytest.raises(CommunicationError):
|
||||
await tester.server.send_message(_Message(_MessageType.TEST_SLOW))
|
||||
|
||||
@ -448,7 +448,7 @@ def test_client_interrupt() -> None:
|
||||
await asyncio.sleep(0.2)
|
||||
tester.client.endpoint.close()
|
||||
|
||||
asyncio.create_task(_kill_connection())
|
||||
_task = asyncio.create_task(_kill_connection())
|
||||
with pytest.raises(CommunicationError):
|
||||
await tester.server.send_message(_Message(_MessageType.TEST_SLOW))
|
||||
|
||||
|
||||
@ -429,15 +429,12 @@ def _sync_server_files(cfg: Config) -> None:
|
||||
outfilename=os.path.join(cfg.serverdst, 'config_template.yaml'),
|
||||
)
|
||||
if cfg.win_type is not None:
|
||||
fname = 'launch_ballisticacore_server.bat'
|
||||
stage_server_file(
|
||||
projroot=cfg.projroot,
|
||||
mode=modeval,
|
||||
infilename=(
|
||||
f'{cfg.src}/../src/server/launch_ballisticacore_server.bat'
|
||||
),
|
||||
outfilename=os.path.join(
|
||||
cfg.serverdst, 'launch_ballisticacore_server.bat'
|
||||
),
|
||||
infilename=f'{cfg.src}/../src/server/{fname}',
|
||||
outfilename=os.path.join(cfg.serverdst, fname),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@ -183,9 +183,11 @@ def lazy_increment_build() -> None:
|
||||
except FileNotFoundError:
|
||||
lasthash = ''
|
||||
if codehash != lasthash:
|
||||
print(f'{Clr.SMAG}Source(s) changed; incrementing build...{Clr.RST}')
|
||||
|
||||
if not update_hash_only:
|
||||
print(
|
||||
f'{Clr.SMAG}Source(s) changed; incrementing build...{Clr.RST}'
|
||||
)
|
||||
# Just go ahead and bless; this will increment the build as needed.
|
||||
# subprocess.run(['make', 'bless'], check=True)
|
||||
subprocess.run(
|
||||
@ -1029,11 +1031,10 @@ def win_ci_install_prereqs() -> None:
|
||||
# build to succeed. Normally this would happen through our Makefile
|
||||
# targets but we can't use them under raw window so we need to just
|
||||
# hard-code whatever we need here.
|
||||
lib_dbg_win32 = 'build/prefab/lib/windows/Debug_Win32'
|
||||
needed_targets: set[str] = {
|
||||
'build/prefab/lib/windows/Debug_Win32/'
|
||||
'BallisticaCoreGenericInternal.lib',
|
||||
'build/prefab/lib/windows/Debug_Win32/'
|
||||
'BallisticaCoreGenericInternal.pdb',
|
||||
f'{lib_dbg_win32}/BallisticaCoreGenericInternal.lib',
|
||||
f'{lib_dbg_win32}/BallisticaCoreGenericInternal.pdb',
|
||||
'ballisticacore-windows/Generic/BallisticaCore.ico',
|
||||
}
|
||||
|
||||
|
||||
@ -703,8 +703,7 @@ class Updater:
|
||||
|
||||
# CMake android components:
|
||||
fname = (
|
||||
'ballisticacore-android/BallisticaCore'
|
||||
'/src/main/cpp/CMakeLists.txt'
|
||||
'ballisticacore-android/BallisticaCore/src/main/cpp/CMakeLists.txt'
|
||||
)
|
||||
if not self._public:
|
||||
self._update_cmake_file(fname)
|
||||
|
||||
@ -44,7 +44,7 @@ def _gen_enums(infilename: str) -> str:
|
||||
out += f'\n\nclass {enum_name}(Enum):\n """'
|
||||
out += '\n '.join(doclines)
|
||||
if len(doclines) > 1:
|
||||
out += '\n """\n'
|
||||
out += '\n """\n\n'
|
||||
else:
|
||||
out += '"""\n'
|
||||
|
||||
|
||||
@ -27,7 +27,9 @@ ABS_MAX_LEVEL = 10
|
||||
# we're showing some temporary objects that we should be ignoring.
|
||||
|
||||
|
||||
def getobjs(cls: type | str, contains: str | None = None) -> list[Any]:
|
||||
def getobjs(
|
||||
cls: type | str, contains: str | None = None, expanded: bool = False
|
||||
) -> list[Any]:
|
||||
"""Return all garbage-collected objects matching criteria.
|
||||
|
||||
'type' can be an actual type or a string in which case objects
|
||||
@ -45,17 +47,51 @@ def getobjs(cls: type | str, contains: str | None = None) -> list[Any]:
|
||||
if not isinstance(contains, str | None):
|
||||
raise TypeError('Expected a string or None for contains')
|
||||
|
||||
allobjs = _get_all_objects(expanded=expanded)
|
||||
|
||||
if isinstance(cls, str):
|
||||
objs = [o for o in gc.get_objects() if cls in str(type(o))]
|
||||
objs = [o for o in allobjs if cls in str(type(o))]
|
||||
else:
|
||||
objs = [o for o in gc.get_objects() if isinstance(o, cls)]
|
||||
objs = [o for o in allobjs if isinstance(o, cls)]
|
||||
if contains is not None:
|
||||
objs = [o for o in objs if contains in str(o)]
|
||||
|
||||
return objs
|
||||
|
||||
|
||||
def getobj(objid: int) -> Any:
|
||||
# Recursively expand slists objects into olist, using seen to track
|
||||
# already processed objects.
|
||||
def _getr(slist: list[Any], olist: list[Any], seen: set[int]) -> None:
|
||||
for obj in slist:
|
||||
if id(obj) in seen:
|
||||
continue
|
||||
seen.add(id(obj))
|
||||
olist.append(obj)
|
||||
tll = gc.get_referents(obj)
|
||||
if tll:
|
||||
_getr(tll, olist, seen)
|
||||
|
||||
|
||||
def _get_all_objects(expanded: bool) -> list[Any]:
|
||||
"""Return an expanded list of all objects.
|
||||
|
||||
See https://utcc.utoronto.ca/~cks/space/blog/python/GetAllObjects
|
||||
"""
|
||||
gcl = gc.get_objects()
|
||||
if not expanded:
|
||||
return gcl
|
||||
olist: list[Any] = []
|
||||
seen: set[int] = set()
|
||||
# Just in case:
|
||||
seen.add(id(gcl))
|
||||
seen.add(id(olist))
|
||||
seen.add(id(seen))
|
||||
# _getr does the real work.
|
||||
_getr(gcl, olist, seen)
|
||||
return olist
|
||||
|
||||
|
||||
def getobj(objid: int, expanded: bool = False) -> Any:
|
||||
"""Return a garbage-collected object by its id.
|
||||
|
||||
Remember that this is VERY inefficient and should only ever be used
|
||||
@ -65,7 +101,10 @@ def getobj(objid: int) -> Any:
|
||||
raise TypeError(f'Expected an int for objid; got a {type(objid)}.')
|
||||
|
||||
# Don't wanna return stuff waiting to be garbage-collected.
|
||||
for obj in gc.get_objects():
|
||||
gc.collect()
|
||||
|
||||
allobjs = _get_all_objects(expanded=expanded)
|
||||
for obj in allobjs:
|
||||
if id(obj) == objid:
|
||||
return obj
|
||||
raise RuntimeError(f'Object with id {objid} not found.')
|
||||
@ -145,12 +184,14 @@ def printrefs(
|
||||
)
|
||||
|
||||
|
||||
def printtypes(limit: int = 50, file: TextIO | None = None) -> None:
|
||||
def printtypes(
|
||||
limit: int = 50, file: TextIO | None = None, expanded: bool = False
|
||||
) -> None:
|
||||
"""Print a human readable list of which types have the most instances."""
|
||||
assert limit > 0
|
||||
objtypes: dict[str, int] = {}
|
||||
gc.collect() # Recommended before get_objects().
|
||||
allobjs = gc.get_objects()
|
||||
allobjs = _get_all_objects(expanded=expanded)
|
||||
allobjc = len(allobjs)
|
||||
for obj in allobjs:
|
||||
modname = type(obj).__module__
|
||||
@ -175,6 +216,38 @@ def printtypes(limit: int = 50, file: TextIO | None = None) -> None:
|
||||
print(f'{i+1}: {tpname}: {tpval} ({percent:.2f}%)', file=file)
|
||||
|
||||
|
||||
def printsizes(
|
||||
limit: int = 50, file: TextIO | None = None, expanded: bool = False
|
||||
) -> None:
|
||||
"""Print total allocated sizes of different types."""
|
||||
assert limit > 0
|
||||
objsizes: dict[str, int] = {}
|
||||
gc.collect() # Recommended before get_objects().
|
||||
allobjs = _get_all_objects(expanded=expanded)
|
||||
totalobjsize = 0
|
||||
|
||||
for obj in allobjs:
|
||||
modname = type(obj).__module__
|
||||
tpname = type(obj).__qualname__
|
||||
if modname != 'builtins':
|
||||
tpname = f'{modname}.{tpname}'
|
||||
objsize = sys.getsizeof(obj)
|
||||
objsizes[tpname] = objsizes.get(tpname, 0) + objsize
|
||||
totalobjsize += objsize
|
||||
|
||||
totalobjmb = totalobjsize / (1024 * 1024)
|
||||
print(
|
||||
f'Types with most allocated bytes ({totalobjmb:.2f} mb total):',
|
||||
file=file,
|
||||
)
|
||||
for i, tpitem in enumerate(
|
||||
sorted(objsizes.items(), key=lambda x: x[1], reverse=True)[:limit]
|
||||
):
|
||||
tpname, tpval = tpitem
|
||||
percent = tpval / totalobjsize * 100.0
|
||||
print(f'{i+1}: {tpname}: {tpval} ({percent:.2f}%)', file=file)
|
||||
|
||||
|
||||
def _desctype(obj: Any) -> str:
|
||||
cls = type(obj)
|
||||
if cls is types.ModuleType:
|
||||
|
||||
@ -292,7 +292,8 @@ class LogHandler(logging.Handler):
|
||||
self._file_chunk_ship_task[
|
||||
name
|
||||
] = self._event_loop.create_task(
|
||||
self._ship_chunks_task(name)
|
||||
self._ship_chunks_task(name),
|
||||
name='log ship file chunks',
|
||||
)
|
||||
|
||||
except Exception:
|
||||
@ -321,7 +322,6 @@ class LogHandler(logging.Handler):
|
||||
traceback.print_exc(file=self._echofile)
|
||||
|
||||
async def _ship_chunks_task(self, name: str) -> None:
|
||||
await asyncio.sleep(0.1)
|
||||
self._ship_file_chunks(name, cancel_ship_task=False)
|
||||
|
||||
def _ship_file_chunks(self, name: str, cancel_ship_task: bool) -> None:
|
||||
|
||||
@ -88,6 +88,10 @@ def ssl_stream_writer_force_close_check(writer: asyncio.StreamWriter) -> None:
|
||||
from efro.call import tpartial
|
||||
from threading import Thread
|
||||
|
||||
# Disabling for now..
|
||||
if bool(True):
|
||||
return
|
||||
|
||||
# Hopefully can remove this in Python 3.11?...
|
||||
# see issue with is_closing() below for more details.
|
||||
transport = getattr(writer, '_transport', None)
|
||||
@ -128,7 +132,9 @@ class _InFlightMessage:
|
||||
def __init__(self) -> None:
|
||||
self._response: bytes | None = None
|
||||
self._got_response = asyncio.Event()
|
||||
self.wait_task = asyncio.create_task(self._wait())
|
||||
self.wait_task = asyncio.create_task(
|
||||
self._wait(), name='rpc in flight msg wait'
|
||||
)
|
||||
|
||||
async def _wait(self) -> bytes:
|
||||
await self._got_response.wait()
|
||||
@ -185,11 +191,11 @@ class RPCEndpoint:
|
||||
self._handle_raw_message_call = handle_raw_message_call
|
||||
self._reader = reader
|
||||
self._writer = writer
|
||||
self._debug_print = debug_print
|
||||
self._debug_print_io = debug_print_io
|
||||
self.debug_print = debug_print
|
||||
self.debug_print_io = debug_print_io
|
||||
if debug_print_call is None:
|
||||
debug_print_call = print
|
||||
self._debug_print_call: Callable[[str], None] = debug_print_call
|
||||
self.debug_print_call: Callable[[str], None] = debug_print_call
|
||||
self._label = label
|
||||
self._thread = current_thread()
|
||||
self._closing = False
|
||||
@ -207,7 +213,7 @@ class RPCEndpoint:
|
||||
|
||||
# Need to hold weak-refs to these otherwise it creates dep-loops
|
||||
# which keeps us alive.
|
||||
self._tasks: list[weakref.ref[asyncio.Task]] = []
|
||||
self._tasks: list[asyncio.Task] = []
|
||||
|
||||
# When we last got a keepalive or equivalent (time.monotonic value)
|
||||
self._last_keepalive_receive_time: float | None = None
|
||||
@ -217,9 +223,9 @@ class RPCEndpoint:
|
||||
|
||||
self._in_flight_messages: dict[int, _InFlightMessage] = {}
|
||||
|
||||
if self._debug_print:
|
||||
if self.debug_print:
|
||||
peername = self._writer.get_extra_info('peername')
|
||||
self._debug_print_call(
|
||||
self.debug_print_call(
|
||||
f'{self._label}: connected to {peername} at {self._tm()}.'
|
||||
)
|
||||
|
||||
@ -270,16 +276,19 @@ class RPCEndpoint:
|
||||
|
||||
core_tasks = [
|
||||
asyncio.create_task(
|
||||
self._run_core_task('keepalive', self._run_keepalive_task())
|
||||
self._run_core_task('keepalive', self._run_keepalive_task()),
|
||||
name='rpc keepalive',
|
||||
),
|
||||
asyncio.create_task(
|
||||
self._run_core_task('read', self._run_read_task())
|
||||
self._run_core_task('read', self._run_read_task()),
|
||||
name='rpc read',
|
||||
),
|
||||
asyncio.create_task(
|
||||
self._run_core_task('write', self._run_write_task())
|
||||
self._run_core_task('write', self._run_write_task()),
|
||||
name='rpc write',
|
||||
),
|
||||
]
|
||||
self._tasks += [weakref.ref(t) for t in core_tasks]
|
||||
self._tasks += core_tasks
|
||||
|
||||
# Run our core tasks until they all complete.
|
||||
results = await asyncio.gather(*core_tasks, return_exceptions=True)
|
||||
@ -309,8 +318,8 @@ class RPCEndpoint:
|
||||
except Exception:
|
||||
logging.exception('Error closing %s.', self._label)
|
||||
|
||||
if self._debug_print:
|
||||
self._debug_print_call(f'{self._label}: finished.')
|
||||
if self.debug_print:
|
||||
self.debug_print_call(f'{self._label}: finished.')
|
||||
|
||||
async def send_message(
|
||||
self,
|
||||
@ -330,11 +339,23 @@ class RPCEndpoint:
|
||||
override this for a particular message.
|
||||
"""
|
||||
# pylint: disable=too-many-branches
|
||||
|
||||
if self.debug_print_io:
|
||||
self.debug_print_call(
|
||||
f'{self._label}: sending message of size {len(message)}'
|
||||
f' at {self._tm()}.'
|
||||
)
|
||||
|
||||
self._check_env()
|
||||
|
||||
if self._closing:
|
||||
raise CommunicationError('Endpoint is closed.')
|
||||
|
||||
if self.debug_print_io:
|
||||
self.debug_print_call(
|
||||
f'{self._label}: have peerinfo? {self._peer_info is not None}.'
|
||||
)
|
||||
|
||||
# We need to know their protocol, so if we haven't gotten a handshake
|
||||
# from them yet, just wait.
|
||||
while self._peer_info is None:
|
||||
@ -349,6 +370,11 @@ class RPCEndpoint:
|
||||
message_id = self._next_message_id
|
||||
self._next_message_id = (self._next_message_id + 1) % 65536
|
||||
|
||||
if self.debug_print_io:
|
||||
self.debug_print_call(
|
||||
f'{self._label}: will enqueue at {self._tm()}.'
|
||||
)
|
||||
|
||||
# FIXME - should handle backpressure (waiting here if there are
|
||||
# enough packets already enqueued).
|
||||
|
||||
@ -371,13 +397,19 @@ class RPCEndpoint:
|
||||
+ message
|
||||
)
|
||||
|
||||
if self.debug_print_io:
|
||||
self.debug_print_call(
|
||||
f'{self._label}: enqueued message of size {len(message)}'
|
||||
f' at {self._tm()}.'
|
||||
)
|
||||
|
||||
# Make an entry so we know this message is out there.
|
||||
assert message_id not in self._in_flight_messages
|
||||
msgobj = self._in_flight_messages[message_id] = _InFlightMessage()
|
||||
|
||||
# Also add its task to our list so we properly cancel it if we die.
|
||||
self._prune_tasks() # Keep our list from filling with dead tasks.
|
||||
self._tasks.append(weakref.ref(msgobj.wait_task))
|
||||
self._tasks.append(msgobj.wait_task)
|
||||
|
||||
# Note: we always want to incorporate a timeout. Individual
|
||||
# messages may hang or error on the other end and this ensures
|
||||
@ -392,16 +424,17 @@ class RPCEndpoint:
|
||||
# Question: we assume this means the above wait_for() was
|
||||
# cancelled; what happens if a task running *us* is cancelled
|
||||
# though?
|
||||
if self._debug_print:
|
||||
self._debug_print_call(
|
||||
if self.debug_print:
|
||||
self.debug_print_call(
|
||||
f'{self._label}: message {message_id} was cancelled.'
|
||||
)
|
||||
if close_on_error:
|
||||
self.close()
|
||||
|
||||
raise CommunicationError() from exc
|
||||
except asyncio.TimeoutError as exc:
|
||||
if self._debug_print:
|
||||
self._debug_print_call(
|
||||
if self.debug_print:
|
||||
self.debug_print_call(
|
||||
f'{self._label}: message {message_id} timed out.'
|
||||
)
|
||||
|
||||
@ -424,21 +457,21 @@ class RPCEndpoint:
|
||||
if self._closing:
|
||||
return
|
||||
|
||||
if self._debug_print:
|
||||
self._debug_print_call(f'{self._label}: closing...')
|
||||
if self.debug_print:
|
||||
self.debug_print_call(f'{self._label}: closing...')
|
||||
|
||||
self._closing = True
|
||||
|
||||
# Kill all of our in-flight tasks.
|
||||
if self._debug_print:
|
||||
self._debug_print_call(f'{self._label}: cancelling tasks...')
|
||||
if self.debug_print:
|
||||
self.debug_print_call(f'{self._label}: cancelling tasks...')
|
||||
for task in self._get_live_tasks():
|
||||
task.cancel()
|
||||
|
||||
# Close our writer.
|
||||
assert not self._did_close_writer
|
||||
if self._debug_print:
|
||||
self._debug_print_call(f'{self._label}: closing writer...')
|
||||
if self.debug_print:
|
||||
self.debug_print_call(f'{self._label}: closing writer...')
|
||||
self._writer.close()
|
||||
self._did_close_writer = True
|
||||
|
||||
@ -474,8 +507,13 @@ class RPCEndpoint:
|
||||
)
|
||||
|
||||
live_tasks = self._get_live_tasks()
|
||||
if self._debug_print:
|
||||
self._debug_print_call(
|
||||
|
||||
# Don't need our task list anymore; this should
|
||||
# break any cyclical refs from tasks referring to us.
|
||||
self._tasks = []
|
||||
|
||||
if self.debug_print:
|
||||
self.debug_print_call(
|
||||
f'{self._label}: waiting for tasks to finish: '
|
||||
f' ({live_tasks=})...'
|
||||
)
|
||||
@ -498,8 +536,8 @@ class RPCEndpoint:
|
||||
id(self),
|
||||
)
|
||||
|
||||
if self._debug_print:
|
||||
self._debug_print_call(
|
||||
if self.debug_print:
|
||||
self.debug_print_call(
|
||||
f'{self._label}: tasks finished; waiting for writer close...'
|
||||
)
|
||||
|
||||
@ -515,15 +553,19 @@ class RPCEndpoint:
|
||||
# indefinitely. See https://github.com/python/cpython/issues/83939
|
||||
# It sounds like this should be fixed in 3.11 but for now just
|
||||
# forcing the issue with a timeout here.
|
||||
await asyncio.wait_for(self._writer.wait_closed(), timeout=30.0)
|
||||
await asyncio.wait_for(
|
||||
self._writer.wait_closed(),
|
||||
# timeout=60.0 * 6.0,
|
||||
timeout=30.0,
|
||||
)
|
||||
except asyncio.TimeoutError:
|
||||
logging.info(
|
||||
'Timeout on _writer.wait_closed() for %s rpc (transport=%s).',
|
||||
self._label,
|
||||
ssl_stream_writer_underlying_transport_info(self._writer),
|
||||
)
|
||||
if self._debug_print:
|
||||
self._debug_print_call(
|
||||
if self.debug_print:
|
||||
self.debug_print_call(
|
||||
f'{self._label}: got timeout in _writer.wait_closed();'
|
||||
' This should be fixed in future Python versions.'
|
||||
)
|
||||
@ -531,8 +573,8 @@ class RPCEndpoint:
|
||||
if not self._is_expected_connection_error(exc):
|
||||
logging.exception('Error closing _writer for %s.', self._label)
|
||||
else:
|
||||
if self._debug_print:
|
||||
self._debug_print_call(
|
||||
if self.debug_print:
|
||||
self.debug_print_call(
|
||||
f'{self._label}: silently ignoring error in'
|
||||
f' _writer.wait_closed(): {exc}.'
|
||||
)
|
||||
@ -555,14 +597,18 @@ class RPCEndpoint:
|
||||
self._check_env()
|
||||
assert self._peer_info is None
|
||||
|
||||
# Bug fix: if we don't have this set we will never time out
|
||||
# if we never receive any data from the other end.
|
||||
self._last_keepalive_receive_time = time.monotonic()
|
||||
|
||||
# The first thing they should send us is their handshake; then
|
||||
# we'll know if/how we can talk to them.
|
||||
mlen = await self._read_int_32()
|
||||
message = await self._reader.readexactly(mlen)
|
||||
self._peer_info = dataclass_from_json(_PeerInfo, message.decode())
|
||||
self._last_keepalive_receive_time = time.monotonic()
|
||||
if self._debug_print:
|
||||
self._debug_print_call(
|
||||
if self.debug_print:
|
||||
self.debug_print_call(
|
||||
f'{self._label}: received handshake at {self._tm()}.'
|
||||
)
|
||||
|
||||
@ -576,8 +622,8 @@ class RPCEndpoint:
|
||||
raise RuntimeError('Got multiple handshakes')
|
||||
|
||||
if mtype is _PacketType.KEEPALIVE:
|
||||
if self._debug_print_io:
|
||||
self._debug_print_call(
|
||||
if self.debug_print_io:
|
||||
self.debug_print_call(
|
||||
f'{self._label}: received keepalive'
|
||||
f' at {self._tm()}.'
|
||||
)
|
||||
@ -606,8 +652,8 @@ class RPCEndpoint:
|
||||
else:
|
||||
msglen = await self._read_int_16()
|
||||
msg = await self._reader.readexactly(msglen)
|
||||
if self._debug_print_io:
|
||||
self._debug_print_call(
|
||||
if self.debug_print_io:
|
||||
self.debug_print_call(
|
||||
f'{self._label}: received message {msgid}'
|
||||
f' of size {msglen} at {self._tm()}.'
|
||||
)
|
||||
@ -617,14 +663,13 @@ class RPCEndpoint:
|
||||
assert not self._closing
|
||||
self._prune_tasks() # Keep from filling with dead tasks.
|
||||
self._tasks.append(
|
||||
weakref.ref(
|
||||
asyncio.create_task(
|
||||
self._handle_raw_message(message_id=msgid, message=msg)
|
||||
)
|
||||
asyncio.create_task(
|
||||
self._handle_raw_message(message_id=msgid, message=msg),
|
||||
name='efro rpc message handle',
|
||||
)
|
||||
)
|
||||
if self._debug_print:
|
||||
self._debug_print_call(
|
||||
if self.debug_print:
|
||||
self.debug_print_call(
|
||||
f'{self._label}: done handling message at {self._tm()}.'
|
||||
)
|
||||
|
||||
@ -636,8 +681,8 @@ class RPCEndpoint:
|
||||
rsplen = await self._read_int_32()
|
||||
else:
|
||||
rsplen = await self._read_int_16()
|
||||
if self._debug_print_io:
|
||||
self._debug_print_call(
|
||||
if self.debug_print_io:
|
||||
self.debug_print_call(
|
||||
f'{self._label}: received response {msgid}'
|
||||
f' of size {rsplen} at {self._tm()}.'
|
||||
)
|
||||
@ -647,8 +692,8 @@ class RPCEndpoint:
|
||||
# It's possible for us to get a response to a message
|
||||
# that has timed out. In this case we will have no local
|
||||
# record of it.
|
||||
if self._debug_print:
|
||||
self._debug_print_call(
|
||||
if self.debug_print:
|
||||
self.debug_print_call(
|
||||
f'{self._label}: got response for nonexistent'
|
||||
f' message id {msgid}; perhaps it timed out?'
|
||||
)
|
||||
@ -730,9 +775,9 @@ class RPCEndpoint:
|
||||
and now - self._last_keepalive_receive_time
|
||||
> self._keepalive_timeout
|
||||
):
|
||||
if self._debug_print:
|
||||
if self.debug_print:
|
||||
since = now - self._last_keepalive_receive_time
|
||||
self._debug_print_call(
|
||||
self.debug_print_call(
|
||||
f'{self._label}: reached keepalive time-out'
|
||||
f' ({since:.1f}s).'
|
||||
)
|
||||
@ -751,15 +796,15 @@ class RPCEndpoint:
|
||||
tasklabel,
|
||||
)
|
||||
else:
|
||||
if self._debug_print:
|
||||
self._debug_print_call(
|
||||
if self.debug_print:
|
||||
self.debug_print_call(
|
||||
f'{self._label}: {tasklabel} task will exit cleanly'
|
||||
f' due to {exc!r}.'
|
||||
)
|
||||
finally:
|
||||
# Any core task exiting triggers shutdown.
|
||||
if self._debug_print:
|
||||
self._debug_print_call(
|
||||
if self.debug_print:
|
||||
self.debug_print_call(
|
||||
f'{self._label}: {tasklabel} task exiting...'
|
||||
)
|
||||
self.close()
|
||||
@ -836,8 +881,8 @@ class RPCEndpoint:
|
||||
"""Enqueue a raw packet to be sent. Must be called from our loop."""
|
||||
self._check_env()
|
||||
|
||||
if self._debug_print_io:
|
||||
self._debug_print_call(
|
||||
if self.debug_print_io:
|
||||
self.debug_print_call(
|
||||
f'{self._label}: enqueueing outgoing packet'
|
||||
f' {data[:50]!r} at {self._tm()}.'
|
||||
)
|
||||
@ -847,17 +892,7 @@ class RPCEndpoint:
|
||||
self._have_out_packets.set()
|
||||
|
||||
def _prune_tasks(self) -> None:
|
||||
out: list[weakref.ref[asyncio.Task]] = []
|
||||
for task_weak_ref in self._tasks:
|
||||
task = task_weak_ref()
|
||||
if task is not None and not task.done():
|
||||
out.append(task_weak_ref)
|
||||
self._tasks = out
|
||||
self._tasks = self._get_live_tasks()
|
||||
|
||||
def _get_live_tasks(self) -> list[asyncio.Task]:
|
||||
out: list[asyncio.Task] = []
|
||||
for task_weak_ref in self._tasks:
|
||||
task = task_weak_ref()
|
||||
if task is not None and not task.done():
|
||||
out.append(task)
|
||||
return out
|
||||
return [t for t in self._tasks if not t.done()]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user