This commit is contained in:
Eric 2022-10-15 21:31:06 -07:00
parent 25c3f2ee01
commit 645e1b3eac
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
23 changed files with 420 additions and 242 deletions

View File

@ -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"

View File

@ -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>

View File

@ -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.

View File

@ -1 +1 @@
180157672676216986210895241045962795292
31242320059036633417109806113241486230

View File

@ -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

View File

@ -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',

View File

@ -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,

View File

@ -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(

View File

@ -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."""

View File

@ -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

View File

@ -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
)

View File

@ -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')

View File

@ -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>

View File

@ -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.

View File

@ -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"

View File

@ -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))

View File

@ -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),
)

View File

@ -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',
}

View File

@ -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)

View File

@ -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'

View File

@ -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:

View File

@ -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:

View File

@ -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()]