prepping for workspaces

This commit is contained in:
Eric Froemling 2022-06-15 12:42:22 -07:00
parent ff37609b83
commit 670bd9d4fd
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
18 changed files with 356 additions and 239 deletions

View File

@ -3992,50 +3992,50 @@
"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",
"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/bf/de/08804e3ad0f319521b1831dedb8b",
"build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/d6/8c/13132c50cc5613d8a1b63ae2b668",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/eb/9e/f7b96cb44899e45c8887e6b0a541",
"build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/33/b5/94df2669c0972cb67f6d5a0deda2",
"build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/e5/50/55227dbd30219b2d85b91b3cb12e",
"build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/d6/0e/fe82f0f783da43ad915b8907f84d",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/33/42/e0081d7e62f3d414330d48773064",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/71/39/33283a49ffd8ea1a1df1630e6960",
"build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/3a/48/2a620297639e4cd77e7c47a31e6b",
"build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/37/57/07794ce711c7c156bce78a51c485",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/1d/bb/72d4eb6505c35d86371e3492963f",
"build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/2c/3e/c85c3d7d2834fc5d1e0abdc49fb9",
"build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/4f/e7/da0f7ae7fb279bc8862b78c14e45",
"build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/ad/49/17db1cebd712f82af2639658e3f3",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/2b/83/7ff2c4de06eb943e83ffdc8f17c6",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/b4/d7/4d794ad4a56a1e46359d13c44123",
"build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/ce/5c/df22430468506b5a2ed6172f326a",
"build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/92/71/5301a16ba18e72504d10d0f72ce0",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/6e/e0/6bba7b32ead0a56ddace4c0f3515",
"build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/b8/f9/282a87b6d4ed616c2e2d174f6ffb",
"build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/72/9d/7041244c1532e8739f4144a0dbdb",
"build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/f9/d3/43568f3cfbf7b9e1f1c0a80d3cbb",
"build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/1d/d7/9508dda5f67d8120159fc27e5aa6",
"build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/32/19/422f06dd741d5fecd501192345db",
"build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a3/e0/532750ec3447a0d2507297ef7e06",
"build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/07/f4/07c76f58e65817f064a6f8eb947a",
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/20/fb/ceab54e3526c6754978d599feea3",
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7e/87/5534e303798640c45eebb3ff2a78",
"build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ec/71/2b6c9e9920857db9d4f08e13a147",
"build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d4/cd/475d49a92f27e1023c4c9929f229",
"build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/23/02/efb19178a0c9f32e7dd1a74c6e0f",
"build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/6c/8b/239dd8045dbdd3f674557dcea6f6",
"build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/eb/28/f681d90ec4fc218081d27a54b524",
"build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b3/5e/11e126f42e97c2ad8fbbdedbddeb",
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/32/80/f5b20ad75c2822c99472832588f0",
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ae/bc/1dfe7e5f4991b790eb28b4655b97",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/22/90/e3e1510d320434dce98c2f2f4bd7",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/41/83/cc82f715fd3b766b8e99cd3e47f3",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/3b/01/b0131177be78b774f7645a25d9fa",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/74/53/922f030cdb5f6631f30d9918cdac",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/71/20/4a9cd20befe66687c3409577dbfe",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/fd/d4/b0984f40c5c005e1ba9c9b5c0b6b",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/3a/2c/2ae55b591d38a78d82a8d929ec45",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/dc/fa/b8cd1ec4ec21748c8cdecd1d857e",
"build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/5e/d0/559c7203ab5e64e2425b971162b9",
"build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/dc/e6/cdef333cc7117b19a5e10ed36de0",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/02/03/c4f8ec99521e7e17676b248cf4aa",
"build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/09/ac/e9ff2d475c39d838ed1841e38616",
"build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/13/2a/853133ad6f735dc18f02474c1ef4",
"build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/a7/d7/ef66ddf0d143691238b6a6f380ca",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/77/20/c42c00d6d900cfa93b4be9c0d272",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/b5/2c/393c5a2e020f8ba53c4ee6d64273",
"build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/f9/be/a4637598a814486b22a63cc73d73",
"build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/19/97/b576a0683093a41ca9de4ded529c",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/d5/1e/fff6a170c50f40b21ce89cfba0c6",
"build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e5/5e/3aae36d786cf5fceeb92ef08be09",
"build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/94/cc/380b688c36b5ab71d58c2ffadce8",
"build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/c3/dc/f1bbcd1052ca7625108cdf5ea651",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/19/bb/92ab2df3c40578d6b55439af3a7a",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/1b/c6/48b825be351cd70902713413484f",
"build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/b0/d5/046011a1c101ff4444f258757650",
"build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/bc/be/52a4a6f939cda096692e9d9e9420",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/7d/5e/e174ad517b1b56aeda59cc946d23",
"build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/6d/da/8deb6def85dee6da1df55ab0d61e",
"build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7a/d7/9868328009e75fd5262b750b2f9f",
"build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b9/e0/488348d0a48c6d2eaec9f166a68a",
"build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e4/c6/bd920458d567ff5519cc402ef0bc",
"build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/97/3e/8d862f251f36e3d43f83372e52e8",
"build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/15/1d/82e224d5a165e01e0a2b675a9f3d",
"build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/70/28/c5da6acadd31a12098c0d90ced65",
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/c2/dd/6b8bbbe6cd25382a64d114f8b985",
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/73/16/fffb5234d28b5f331db364c1cf6b",
"build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/3d/e8/b425dd250a1ae01a4677baff7582",
"build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b5/aa/a7fd6fbe303107be212ca18d19da",
"build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/bb/da/d37b08e2130ac35ae4857948ce6f",
"build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/8b/e7/a4f4fb02098dce00f89214003293",
"build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e1/f7/a49851aac3e3fa58da5471ae9db8",
"build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/0a/aa/ac941fe32bcdfa985949c5e5bf79",
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/36/f4/fe7557615bd02e750e54f6c18443",
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a5/de/a7047d5d833f8ebb62e383e5eaf4",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/ce/9c/e473fc15f9d3a0bedfb3913d1962",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/3f/ed/28da613b3e324d1430b237fb08a6",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/0a/7f/2cdc32fd26567e699debe0bc2664",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/a8/df/b80403b15d88e26fd33c37c0941f",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/48/98/24c3d9f63d6909320ee6152b2225",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/3d/1e/dc89ecda37bcf6ce72e8d19452d9",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/e9/ce/8b528bcfdc5dabd974c4693ccd63",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/34/33/2d5a7cff960d15324c98199a2d39",
"src/ballistica/generated/python_embedded/binding.inc": "https://files.ballistica.net/cache/ba1/6e/6f/004b696e9a13b083069374e4bb6a",
"src/ballistica/generated/python_embedded/bootstrap.inc": "https://files.ballistica.net/cache/ba1/d3/db/e73d4dcf1280d5f677c3cf8b47c3"
}

View File

@ -600,6 +600,7 @@
<w>dincrease</w>
<w>dingsound</w>
<w>dingsoundhigh</w>
<w>dinl</w>
<w>dirfilter</w>
<w>dirmanifest</w>
<w>dirname</w>
@ -872,6 +873,7 @@
<w>floinkdingle</w>
<w>floof</w>
<w>floofcls</w>
<w>floooff</w>
<w>floop</w>
<w>flycheck</w>
<w>fmod</w>
@ -1391,6 +1393,7 @@
<w>locs</w>
<w>logcat</w>
<w>logincode</w>
<w>loginid</w>
<w>logintoken</w>
<w>logitech</w>
<w>logput</w>
@ -2325,6 +2328,7 @@
<w>startscan</w>
<w>startsplits</w>
<w>starttime</w>
<w>statestr</w>
<w>statictest</w>
<w>statictestfiles</w>
<w>statictype</w>
@ -2481,6 +2485,7 @@
<w>testpatch</w>
<w>testpath</w>
<w>testpt</w>
<w>testresponse</w>
<w>testsealable</w>
<w>testsentinel</w>
<w>testsoundtrack</w>
@ -2520,6 +2525,7 @@
<w>timetype</w>
<w>tipstext</w>
<w>titletext</w>
<w>tkinval</w>
<w>tlog</w>
<w>tmpdir</w>
<w>tmpf</w>
@ -2592,6 +2598,7 @@
<w>typestr</w>
<w>tzdiff</w>
<w>tzinfos</w>
<w>tzoffset</w>
<w>tzpath</w>
<w>uadfc</w>
<w>uber</w>
@ -2605,6 +2612,7 @@
<w>uicleanupchecks</w>
<w>uicontroller</w>
<w>uilocation</w>
<w>uinl</w>
<w>uiscale</w>
<w>uiupkeeptimer</w>
<w>unallowed</w>

View File

@ -1,4 +1,4 @@
### 1.7.2 (20601, 2022-06-04)
### 1.7.2 (20604, 2022-06-15)
- Minor fixes in some minigames (Thanks Droopy!)
- Fixed a bug preventing 'clients' arg from working in _ba.chatmessage (Thanks imayushsaini!)
- Fixed a bug where ba.Player.getdelegate(doraise=True) could return None instead of raising a ba.DelegateNotFoundError (thanks Dliwk!)

View File

@ -18,6 +18,7 @@
"ba_data/python/ba/__pycache__/_asyncio.cpython-310.opt-1.pyc",
"ba_data/python/ba/__pycache__/_benchmark.cpython-310.opt-1.pyc",
"ba_data/python/ba/__pycache__/_campaign.cpython-310.opt-1.pyc",
"ba_data/python/ba/__pycache__/_cloud.cpython-310.opt-1.pyc",
"ba_data/python/ba/__pycache__/_collision.cpython-310.opt-1.pyc",
"ba_data/python/ba/__pycache__/_coopgame.cpython-310.opt-1.pyc",
"ba_data/python/ba/__pycache__/_coopsession.cpython-310.opt-1.pyc",
@ -59,7 +60,6 @@
"ba_data/python/ba/__pycache__/_tips.cpython-310.opt-1.pyc",
"ba_data/python/ba/__pycache__/_tournament.cpython-310.opt-1.pyc",
"ba_data/python/ba/__pycache__/_ui.cpython-310.opt-1.pyc",
"ba_data/python/ba/__pycache__/cloud.cpython-310.opt-1.pyc",
"ba_data/python/ba/__pycache__/deprecated.cpython-310.opt-1.pyc",
"ba_data/python/ba/__pycache__/internal.cpython-310.opt-1.pyc",
"ba_data/python/ba/__pycache__/macmusicapp.cpython-310.opt-1.pyc",
@ -82,6 +82,7 @@
"ba_data/python/ba/_asyncio.py",
"ba_data/python/ba/_benchmark.py",
"ba_data/python/ba/_campaign.py",
"ba_data/python/ba/_cloud.py",
"ba_data/python/ba/_collision.py",
"ba_data/python/ba/_coopgame.py",
"ba_data/python/ba/_coopsession.py",
@ -127,7 +128,6 @@
"ba_data/python/ba/_tips.py",
"ba_data/python/ba/_tournament.py",
"ba_data/python/ba/_ui.py",
"ba_data/python/ba/cloud.py",
"ba_data/python/ba/deprecated.py",
"ba_data/python/ba/internal.py",
"ba_data/python/ba/macmusicapp.py",
@ -143,12 +143,14 @@
"ba_data/python/bacommon/__pycache__/cloud.cpython-310.opt-1.pyc",
"ba_data/python/bacommon/__pycache__/net.cpython-310.opt-1.pyc",
"ba_data/python/bacommon/__pycache__/servermanager.cpython-310.opt-1.pyc",
"ba_data/python/bacommon/__pycache__/transfer.cpython-310.opt-1.pyc",
"ba_data/python/bacommon/assets.py",
"ba_data/python/bacommon/bacloud.py",
"ba_data/python/bacommon/build.py",
"ba_data/python/bacommon/cloud.py",
"ba_data/python/bacommon/net.py",
"ba_data/python/bacommon/servermanager.py",
"ba_data/python/bacommon/transfer.py",
"ba_data/python/bastd/__init__.py",
"ba_data/python/bastd/__pycache__/__init__.cpython-310.opt-1.pyc",
"ba_data/python/bastd/__pycache__/appdelegate.cpython-310.opt-1.pyc",

View File

@ -151,6 +151,7 @@ SCRIPT_TARGETS_PY_PUBLIC = \
build/ba_data/python/ba/_asyncio.py \
build/ba_data/python/ba/_benchmark.py \
build/ba_data/python/ba/_campaign.py \
build/ba_data/python/ba/_cloud.py \
build/ba_data/python/ba/_collision.py \
build/ba_data/python/ba/_coopgame.py \
build/ba_data/python/ba/_coopsession.py \
@ -194,7 +195,6 @@ SCRIPT_TARGETS_PY_PUBLIC = \
build/ba_data/python/ba/_tips.py \
build/ba_data/python/ba/_tournament.py \
build/ba_data/python/ba/_ui.py \
build/ba_data/python/ba/cloud.py \
build/ba_data/python/ba/deprecated.py \
build/ba_data/python/ba/internal.py \
build/ba_data/python/ba/macmusicapp.py \
@ -398,6 +398,7 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
build/ba_data/python/ba/__pycache__/_asyncio.cpython-310.opt-1.pyc \
build/ba_data/python/ba/__pycache__/_benchmark.cpython-310.opt-1.pyc \
build/ba_data/python/ba/__pycache__/_campaign.cpython-310.opt-1.pyc \
build/ba_data/python/ba/__pycache__/_cloud.cpython-310.opt-1.pyc \
build/ba_data/python/ba/__pycache__/_collision.cpython-310.opt-1.pyc \
build/ba_data/python/ba/__pycache__/_coopgame.cpython-310.opt-1.pyc \
build/ba_data/python/ba/__pycache__/_coopsession.cpython-310.opt-1.pyc \
@ -441,7 +442,6 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
build/ba_data/python/ba/__pycache__/_tips.cpython-310.opt-1.pyc \
build/ba_data/python/ba/__pycache__/_tournament.cpython-310.opt-1.pyc \
build/ba_data/python/ba/__pycache__/_ui.cpython-310.opt-1.pyc \
build/ba_data/python/ba/__pycache__/cloud.cpython-310.opt-1.pyc \
build/ba_data/python/ba/__pycache__/deprecated.cpython-310.opt-1.pyc \
build/ba_data/python/ba/__pycache__/internal.cpython-310.opt-1.pyc \
build/ba_data/python/ba/__pycache__/macmusicapp.cpython-310.opt-1.pyc \
@ -648,6 +648,7 @@ SCRIPT_TARGETS_PY_PUBLIC_TOOLS = \
build/ba_data/python/bacommon/cloud.py \
build/ba_data/python/bacommon/net.py \
build/ba_data/python/bacommon/servermanager.py \
build/ba_data/python/bacommon/transfer.py \
build/ba_data/python/efro/__init__.py \
build/ba_data/python/efro/call.py \
build/ba_data/python/efro/dataclassio/__init__.py \
@ -677,6 +678,7 @@ SCRIPT_TARGETS_PYC_PUBLIC_TOOLS = \
build/ba_data/python/bacommon/__pycache__/cloud.cpython-310.opt-1.pyc \
build/ba_data/python/bacommon/__pycache__/net.cpython-310.opt-1.pyc \
build/ba_data/python/bacommon/__pycache__/servermanager.cpython-310.opt-1.pyc \
build/ba_data/python/bacommon/__pycache__/transfer.cpython-310.opt-1.pyc \
build/ba_data/python/efro/__pycache__/__init__.cpython-310.opt-1.pyc \
build/ba_data/python/efro/__pycache__/call.cpython-310.opt-1.pyc \
build/ba_data/python/efro/dataclassio/__pycache__/__init__.cpython-310.opt-1.pyc \

View File

@ -24,6 +24,7 @@ from ba._actor import Actor
from ba._player import PlayerInfo, Player, EmptyPlayer, StandLocation
from ba._nodeactor import NodeActor
from ba._app import App
from ba._cloud import CloudSubsystem
from ba._coopgame import CoopGameActivity
from ba._coopsession import CoopSession
from ba._dependency import (Dependency, DependencyComponent, DependencySet,
@ -89,7 +90,7 @@ __all__ = [
'clipboard_get_text', 'clipboard_has_text', 'clipboard_is_supported',
'clipboard_set_text', 'CollideModel', 'Collision', 'columnwidget',
'containerwidget', 'Context', 'ContextCall', 'ContextError',
'CoopGameActivity', 'CoopSession', 'Data', 'DeathType',
'CloudSubsystem', 'CoopGameActivity', 'CoopSession', 'Data', 'DeathType',
'DelegateNotFoundError', 'Dependency', 'DependencyComponent',
'DependencyError', 'DependencySet', 'DieMessage', 'do_once', 'DropMessage',
'DroppedMessage', 'DualTeamSession', 'emitfx', 'EmptyPlayer', 'EmptyTeam',

View File

@ -25,7 +25,7 @@ if TYPE_CHECKING:
from typing import Optional, Any, Callable
import ba
from ba.cloud import CloudSubsystem
from ba._cloud import CloudSubsystem
from bastd.actor import spazappearance
from ba._accountv2 import AccountV2Subsystem

View File

@ -9,7 +9,7 @@ from typing import TYPE_CHECKING, overload
import _ba
if TYPE_CHECKING:
from typing import Union, Callable, Any
from typing import Callable, Any
from efro.message import Message
import bacommon.cloud
@ -35,8 +35,7 @@ class CloudSubsystem:
self,
msg: bacommon.cloud.LoginProxyRequestMessage,
on_response: Callable[
[Union[bacommon.cloud.LoginProxyRequestResponse,
Exception]], None],
[bacommon.cloud.LoginProxyRequestResponse | Exception], None],
) -> None:
...
@ -45,8 +44,7 @@ class CloudSubsystem:
self,
msg: bacommon.cloud.LoginProxyStateQueryMessage,
on_response: Callable[
[Union[bacommon.cloud.LoginProxyStateQueryResponse,
Exception]], None],
[bacommon.cloud.LoginProxyStateQueryResponse | Exception], None],
) -> None:
...
@ -54,7 +52,7 @@ class CloudSubsystem:
def send_message(
self,
msg: bacommon.cloud.LoginProxyCompleteMessage,
on_response: Callable[[Union[None, Exception]], None],
on_response: Callable[[None | Exception], None],
) -> None:
...
@ -63,7 +61,7 @@ class CloudSubsystem:
self,
msg: bacommon.cloud.CredentialsCheckMessage,
on_response: Callable[
[Union[bacommon.cloud.CredentialsCheckResponse, Exception]], None],
[bacommon.cloud.CredentialsCheckResponse | Exception], None],
) -> None:
...
@ -71,7 +69,7 @@ class CloudSubsystem:
def send_message(
self,
msg: bacommon.cloud.AccountSessionReleaseMessage,
on_response: Callable[[Union[None, Exception]], None],
on_response: Callable[[None | Exception], None],
) -> None:
...

View File

@ -317,6 +317,7 @@
<w>dfmt</w>
<w>dictval</w>
<w>diffbit</w>
<w>dinl</w>
<w>dirfilter</w>
<w>dirslash</w>
<w>dlfcn</w>
@ -441,6 +442,7 @@
<w>fjcoiwef</w>
<w>flipbit</w>
<w>floinkdingle</w>
<w>floooff</w>
<w>floop</w>
<w>flopsy</w>
<w>fname</w>
@ -683,6 +685,7 @@
<w>lockstr</w>
<w>locktype</w>
<w>logincode</w>
<w>loginid</w>
<w>logmsg</w>
<w>logpath</w>
<w>logprefix</w>
@ -1186,6 +1189,7 @@
<w>starttime</w>
<w>startx</w>
<w>starty</w>
<w>statestr</w>
<w>staticdata</w>
<w>statictest</w>
<w>stdint</w>
@ -1236,6 +1240,7 @@
<w>testint</w>
<w>testinternalcapi</w>
<w>testnode</w>
<w>testresponse</w>
<w>texel</w>
<w>texqualstr</w>
<w>textcolor</w>
@ -1258,6 +1263,7 @@
<w>timesteps</w>
<w>timetype</w>
<w>timetypes</w>
<w>tkinval</w>
<w>tlog</w>
<w>tmpmat</w>
<w>tomer</w>
@ -1293,12 +1299,14 @@
<w>twst</w>
<w>typeobj</w>
<w>typestr</w>
<w>tzoffset</w>
<w>tzpath</w>
<w>uber</w>
<w>udbz</w>
<w>udif</w>
<w>uibounds</w>
<w>uiid</w>
<w>uinl</w>
<w>unbased</w>
<w>unblessed</w>
<w>uncas</w>

View File

@ -21,7 +21,7 @@
namespace ballistica {
// These are set automatically via script; don't modify them here.
const int kAppBuildNumber = 20601;
const int kAppBuildNumber = 20604;
const char* kAppVersion = "1.7.2";
// Our standalone globals.

View File

@ -7,42 +7,42 @@ This facilitates workflows such as creating asset-packages, etc.
from __future__ import annotations
import sys
import os
import sys
import zlib
import time
import base64
import datetime
import tempfile
import subprocess
from pathlib import Path
from typing import TYPE_CHECKING
from dataclasses import dataclass
import json
import subprocess
import tempfile
from concurrent.futures import ThreadPoolExecutor
import requests
from efro.error import CleanError
from efro.terminal import Clr
from efro.dataclassio import dataclass_from_json
from bacommon.bacloud import Response
from efro.error import CleanError
from efro.dataclassio import (dataclass_from_json, dataclass_to_dict,
dataclass_to_json, ioprepped)
from bacommon.bacloud import RequestData, ResponseData, BACLOUD_VERSION
from bacommon.transfer import DirectoryManifest
if TYPE_CHECKING:
from typing import Optional, BinaryIO, IO
# Version is sent to the master-server with all commands. Can be incremented
# if we need to change behavior server-side to go along with client changes.
VERSION = 2
from typing import BinaryIO, IO
TOOL_NAME = 'bacloud'
# Server we talk to (can override via env var).
MASTER_SERVER_URL = os.getenv('BACLOUD_SERVER_URL',
'https://bamaster.appspot.com')
BACLOUD_SERVER_URL = os.getenv('BACLOUD_SERVER_URL', 'https://ballistica.net')
@ioprepped
@dataclass
class StateData:
"""Persistent state data stored to disk."""
login_token: Optional[str] = None
login_token: str | None = None
def get_tz_offset_seconds() -> float:
@ -53,70 +53,12 @@ def get_tz_offset_seconds() -> float:
return utc_offset
@dataclass
class DirManifestFile:
"""Represents a single file within a DirManifest."""
filehash: str
filesize: int
class DirManifest:
"""Represents a directory of files with some common purpose."""
def __init__(self) -> None:
self.path = Path('')
self.files: dict[str, DirManifestFile] = {}
@classmethod
def load_from_disk(cls, path: Path) -> DirManifest:
"""Create a package populated from a directory on disk."""
package = DirManifest()
package.path = path
packagepathstr = str(path)
paths: list[str] = []
# Simply return empty manifests if the given path isn't a dir.
# (the server may intend to create it and is just asking what's
# there already)
if path.is_dir():
# Build the full list of package-relative paths.
for basename, _dirnames, filenames in os.walk(path):
for filename in filenames:
fullname = os.path.join(basename, filename)
assert fullname.startswith(packagepathstr)
paths.append(fullname[len(packagepathstr) + 1:])
import hashlib
from concurrent.futures import ThreadPoolExecutor
from multiprocessing import cpu_count
def _get_file_info(filepath: str) -> tuple[str, DirManifestFile]:
sha = hashlib.sha256()
fullfilepath = os.path.join(packagepathstr, filepath)
if not os.path.isfile(fullfilepath):
raise Exception(f'File not found: "{fullfilepath}"')
with open(fullfilepath, 'rb') as infile:
filebytes = infile.read()
filesize = len(filebytes)
sha.update(filebytes)
return (filepath,
DirManifestFile(filehash=sha.hexdigest(),
filesize=filesize))
# Now use all procs to hash the files efficiently.
with ThreadPoolExecutor(max_workers=cpu_count()) as executor:
package.files = dict(executor.map(_get_file_info, paths))
return package
class App:
"""Context for a run of the tool."""
def __init__(self) -> None:
self._state = StateData()
self._project_root: Optional[Path] = None
self._project_root: Path | None = None
self._end_command_args: dict = {}
def run(self) -> None:
@ -163,7 +105,7 @@ class App:
return
try:
with open(self._state_data_path, 'r', encoding='utf-8') as infile:
self._state = StateData(**json.loads(infile.read()))
self._state = dataclass_from_json(StateData, infile.read())
except Exception:
print(f'{Clr.RED}Error loading {TOOL_NAME} data;'
f' resetting to defaults.{Clr.RST}')
@ -172,31 +114,35 @@ class App:
if not self._state_dir.exists():
self._state_dir.mkdir(parents=True, exist_ok=True)
with open(self._state_data_path, 'w', encoding='utf-8') as outfile:
outfile.write(json.dumps(self._state.__dict__))
outfile.write(dataclass_to_json(self._state))
def _servercmd(self,
cmd: str,
data: dict,
files: dict[str, IO] = None) -> Response:
payload: dict,
files: dict[str, IO] = None) -> ResponseData:
"""Issue a command to the server and get a response."""
response_raw_2 = requests.post(
(MASTER_SERVER_URL + '/bacloudcmd'),
headers={'User-Agent': f'bacloud/{VERSION}'},
response_raw = requests.post(
f'{BACLOUD_SERVER_URL}/bacloudcmd',
headers={'User-Agent': f'bacloud/{BACLOUD_VERSION}'},
data={
'c': cmd,
'v': VERSION,
't': json.dumps(self._state.login_token),
'd': json.dumps(data),
'z': get_tz_offset_seconds(),
'y': int(sys.stdout.isatty()),
'v':
BACLOUD_VERSION,
'r':
dataclass_to_json(
RequestData(command=cmd,
token=self._state.login_token,
payload=payload,
tzoffset=get_tz_offset_seconds(),
isatty=sys.stdout.isatty())),
},
files=files)
response_raw_2.raise_for_status() # Except if anything went wrong.
assert isinstance(response_raw_2.content, bytes)
files=files,
)
response_raw.raise_for_status() # Error if anything went wrong.
assert isinstance(response_raw.content, bytes)
response = dataclass_from_json(Response,
response_raw_2.content.decode())
response = dataclass_from_json(ResponseData,
response_raw.content.decode())
# Handle a few things inline.
# (so this functionality is available even to recursive commands, etc.)
@ -212,7 +158,7 @@ class App:
return response
def _upload_file(self, filename: str, call: str, args: dict) -> None:
print(f'{Clr.BLU}Uploading {filename}{Clr.RST}', flush=True)
print(f'Uploading {Clr.BLU}{filename}{Clr.RST}', flush=True)
with tempfile.TemporaryDirectory() as tempdir:
srcpath = Path(filename)
gzpath = Path(tempdir, 'file.gz')
@ -228,17 +174,10 @@ class App:
)
def _handle_dir_manifest_response(self, dirmanifest: str) -> None:
from dataclasses import asdict
manifest = DirManifest.load_from_disk(Path(dirmanifest))
# Store the manifest to be included with our next called command.
self._end_command_args['manifest'] = {
'files': {key: asdict(val)
for key, val in manifest.files.items()}
}
self._end_command_args['manifest'] = dataclass_to_dict(
DirectoryManifest.create_from_disk(Path(dirmanifest)))
def _handle_uploads(self, uploads: tuple[list[str], str, dict]) -> None:
from concurrent.futures import ThreadPoolExecutor
assert len(uploads) == 3
filenames, uploadcmd, uploadargs = uploads
assert isinstance(filenames, list)
@ -256,12 +195,29 @@ class App:
# exceptions that occurred.
list(executor.map(_do_filename, filenames))
def _handle_downloads_inline(self, downloads_inline: dict[str,
str]) -> None:
def _handle_deletes(self, deletes: list[str]) -> None:
"""Handle file deletes."""
for fname in deletes:
# Server shouldn't be sending us dir paths here.
assert not os.path.isdir(fname)
os.unlink(fname)
def _handle_downloads_inline(
self,
downloads_inline: dict[str, str],
) -> None:
"""Handle inline file data to be saved to the client."""
import base64
import zlib
for fname, fdata in downloads_inline.items():
# If there's a directory where we want our file to go, clear it
# out first. File deletes should have run before this so
# everything under it should be empty and thus killable via rmdir.
if os.path.isdir(fname):
for basename, dirnames, _fn in os.walk(fname, topdown=False):
for dirname in dirnames:
os.rmdir(os.path.join(basename, dirname))
os.rmdir(fname)
dirname = os.path.dirname(fname)
if dirname:
os.makedirs(dirname, exist_ok=True)
@ -270,26 +226,6 @@ class App:
with open(fname, 'wb') as outfile:
outfile.write(data)
def _handle_deletes(self, deletes: list[str]) -> None:
"""Handle file deletes."""
for fname in deletes:
os.unlink(fname)
def _handle_uploads_inline(self, uploads_inline: list[str]) -> None:
"""Handle uploading files inline."""
import base64
import zlib
files: dict[str, str] = {}
for filepath in uploads_inline:
if not os.path.exists(filepath):
raise CleanError(f'File not found: {filepath}')
with open(filepath, 'rb') as infile:
data = infile.read()
data_zipped = zlib.compress(data)
data_base64 = base64.b64encode(data_zipped).decode()
files[filepath] = data_base64
self._end_command_args['uploads_inline'] = files
def _handle_dir_prune_empty(self, prunedir: str) -> None:
"""Handle pruning empty directories."""
# Walk the tree bottom-up so we can properly kill recursive empty dirs.
@ -304,6 +240,19 @@ class App:
if not dirnames and not filenames and basename != prunedir:
os.rmdir(basename)
def _handle_uploads_inline(self, uploads_inline: list[str]) -> None:
"""Handle uploading files inline."""
files: dict[str, str] = {}
for filepath in uploads_inline:
if not os.path.exists(filepath):
raise CleanError(f'File not found: {filepath}')
with open(filepath, 'rb') as infile:
data = infile.read()
data_zipped = zlib.compress(data)
data_base64 = base64.b64encode(data_zipped).decode()
files[filepath] = data_base64
self._end_command_args['uploads_inline'] = files
def _handle_open_url(self, url: str) -> None:
import webbrowser
webbrowser.open(url)
@ -321,13 +270,14 @@ class App:
"""Run a single user command to completion."""
# pylint: disable=too-many-branches
nextcall: Optional[tuple[str, dict]] = ('user', {'a': args})
nextcall: tuple[str, dict] | None = ('user', {'a': args})
# Now talk to the server in a loop until there's nothing left to do.
while nextcall is not None:
self._end_command_args = {}
response = self._servercmd(*nextcall)
nextcall = None
if response.login is not None:
self._state.login_token = response.login
if response.logout:
@ -338,12 +288,18 @@ class App:
self._handle_uploads_inline(response.uploads_inline)
if response.uploads is not None:
self._handle_uploads(response.uploads)
if response.downloads_inline:
self._handle_downloads_inline(response.downloads_inline)
# Note: we handle file deletes *before* downloads. This
# way our file-download code only has to worry about creating or
# removing directories; not files, and corner cases such as
# a file getting replaced with a directory should just work.
if response.deletes:
self._handle_deletes(response.deletes)
if response.downloads_inline:
self._handle_downloads_inline(response.downloads_inline)
if response.dir_prune_empty:
self._handle_dir_prune_empty(response.dir_prune_empty)
if response.open_url is not None:
self._handle_open_url(response.open_url)
if response.input_prompt is not None:
@ -364,7 +320,7 @@ if __name__ == '__main__':
App().run()
except KeyboardInterrupt:
# Let's do a clean fail on keyboard interrupt.
# Can make this optional if a backtrace is ever useful..
# Can make this optional if a backtrace is ever useful.
sys.exit(1)
except CleanError as clean_exc:
clean_exc.pretty_print()

View File

@ -5,7 +5,7 @@
from __future__ import annotations
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Optional, Annotated
from typing import TYPE_CHECKING, Annotated
from enum import Enum
from efro.dataclassio import ioprepped, IOAttrs
@ -57,4 +57,4 @@ class AssetPackageBuildState:
# Build error string. If this is present, it should be presented
# to the user and they should required to explicitly restart the build
# in some way if desired.
error: Annotated[Optional[str], IOAttrs('e')] = None
error: Annotated[str | None, IOAttrs('e')] = None

View File

@ -3,18 +3,34 @@
"""Functionality related to the bacloud tool."""
from __future__ import annotations
from dataclasses import dataclass
from typing import TYPE_CHECKING, Optional
from efro.dataclassio import ioprepped
from dataclasses import dataclass
from typing import TYPE_CHECKING, Annotated
from efro.dataclassio import ioprepped, IOAttrs
if TYPE_CHECKING:
pass
# Version is sent to the master-server with all commands. Can be incremented
# if we need to change behavior server-side to go along with client changes.
BACLOUD_VERSION = 6
@ioprepped
@dataclass
class Response:
class RequestData:
"""Request sent to bacloud server."""
command: Annotated[str, IOAttrs('c')]
token: Annotated[str | None, IOAttrs('t')]
payload: Annotated[dict, IOAttrs('p')]
tzoffset: Annotated[float, IOAttrs('z')]
isatty: Annotated[bool, IOAttrs('y')]
@ioprepped
@dataclass
class ResponseData:
# noinspection PyUnresolvedReferences
"""Response sent from the bacloud server to the client.
@ -35,10 +51,10 @@ class Response:
uploads_inline: If present, a list of pathnames that should be base64
gzipped and uploaded to an 'uploads_inline' dict in end_command args.
This should be limited to relatively small files.
deletes: If present, file paths that should be deleted on the client.
downloads_inline: If present, pathnames mapped to base64 gzipped data to
be written to the client. This should only be used for relatively
small files as they are all included inline as part of the response.
deletes: If present, file paths that should be deleted on the client.
dir_prune_empty: If present, all empty dirs under this one should be
removed.
open_url: If present, url to display to the user.
@ -52,20 +68,29 @@ class Response:
end_command: If present, this command is run with these args at the end
of response processing.
"""
message: Optional[str] = None
message_end: str = '\n'
error: Optional[str] = None
delay_seconds: float = 0.0
login: Optional[str] = None
logout: bool = False
dir_manifest: Optional[str] = None
uploads: Optional[tuple[list[str], str, dict]] = None
uploads_inline: Optional[list[str]] = None
downloads_inline: Optional[dict[str, str]] = None
deletes: Optional[list[str]] = None
dir_prune_empty: Optional[str] = None
open_url: Optional[str] = None
input_prompt: Optional[tuple[str, bool]] = None
end_message: Optional[str] = None
end_message_end: str = '\n'
end_command: Optional[tuple[str, dict]] = None
message: Annotated[str | None, IOAttrs('m', store_default=False)] = None
message_end: Annotated[str, IOAttrs('m_end', store_default=False)] = '\n'
error: Annotated[str | None, IOAttrs('e', store_default=False)] = None
delay_seconds: Annotated[float, IOAttrs('d', store_default=False)] = 0.0
login: Annotated[str | None, IOAttrs('l', store_default=False)] = None
logout: Annotated[bool, IOAttrs('lo', store_default=False)] = False
dir_manifest: Annotated[str | None,
IOAttrs('man', store_default=False)] = None
uploads: Annotated[tuple[list[str], str, dict] | None,
IOAttrs('u', store_default=False)] = None
uploads_inline: Annotated[list[str] | None,
IOAttrs('uinl', store_default=False)] = None
deletes: Annotated[list[str] | None,
IOAttrs('dlt', store_default=False)] = None
downloads_inline: Annotated[dict[str, str] | None,
IOAttrs('dinl', store_default=False)] = None
dir_prune_empty: Annotated[str | None,
IOAttrs('dpe', store_default=False)] = None
open_url: Annotated[str | None, IOAttrs('url', store_default=False)] = None
input_prompt: Annotated[tuple[str, bool] | None,
IOAttrs('inp', store_default=False)] = None
end_message: Annotated[str | None,
IOAttrs('em', store_default=False)] = None
end_message_end: Annotated[str, IOAttrs('eme', store_default=False)] = '\n'
end_command: Annotated[tuple[str, dict] | None,
IOAttrs('ec', store_default=False)] = None

View File

@ -4,7 +4,7 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Optional, Any, Annotated
from typing import TYPE_CHECKING, Any, Annotated
from dataclasses import dataclass, field
from efro.dataclassio import ioprepped, IOAttrs
@ -28,7 +28,7 @@ class ServerNodeQueryResponse:
"""A response to a query about server-nodes."""
# If present, something went wrong, and this describes it.
error: Annotated[Optional[str], IOAttrs('e', store_default=False)] = None
error: Annotated[str | None, IOAttrs('e', store_default=False)] = None
# The set of servernodes.
servers: Annotated[list[ServerNodeEntry],
@ -40,11 +40,11 @@ class ServerNodeQueryResponse:
@dataclass
class PrivateHostingState:
"""Combined state of whether we're hosting, whether we can, etc."""
unavailable_error: Optional[str] = None
party_code: Optional[str] = None
unavailable_error: str | None = None
party_code: str | None = None
tickets_to_host_now: int = 0
minutes_until_free_host: Optional[float] = None
free_host_minutes_remaining: Optional[float] = None
minutes_until_free_host: float | None = None
free_host_minutes_remaining: float | None = None
@ioprepped
@ -55,10 +55,10 @@ class PrivateHostingConfig:
playlist_name: str = 'Unknown'
randomize: bool = False
tutorial: bool = False
custom_team_names: Optional[tuple[str, str]] = None
custom_team_colors: Optional[tuple[tuple[float, float, float],
tuple[float, float, float]]] = None
playlist: Optional[list[dict[str, Any]]] = None
custom_team_names: tuple[str, str] | None = None
custom_team_colors: tuple[tuple[float, float, float],
tuple[float, float, float]] | None = None
playlist: list[dict[str, Any]] | None = None
exit_minutes: float = 120.0
exit_minutes_unclean: float = 180.0
exit_minutes_idle: float = 10.0
@ -68,7 +68,7 @@ class PrivateHostingConfig:
@dataclass
class PrivatePartyConnectResult:
"""Info about a server we get back when connecting."""
error: Optional[str] = None
addr: Optional[str] = None
port: Optional[int] = None
password: Optional[str] = None
error: str | None = None
addr: str | None = None
port: int | None = None
password: str | None = None

View File

@ -0,0 +1,78 @@
# Released under the MIT License. See LICENSE for details.
#
"""Functionality related to transferring files/data."""
from __future__ import annotations
import os
from dataclasses import dataclass
from typing import TYPE_CHECKING, Annotated
from efro.dataclassio import ioprepped, IOAttrs
if TYPE_CHECKING:
from pathlib import Path
@ioprepped
@dataclass
class DirectoryManifestFile:
"""Describes metadata and hashes for a file in a manifest."""
filehash: Annotated[str, IOAttrs('h')]
filesize: Annotated[int, IOAttrs('s')]
@ioprepped
@dataclass
class DirectoryManifest:
"""Contains a summary of files in a directory."""
files: Annotated[dict[str, DirectoryManifestFile], IOAttrs('f')]
_empty_hash: str | None = None
@classmethod
def create_from_disk(cls, path: Path) -> DirectoryManifest:
"""Create a manifest from a directory on disk."""
import hashlib
from multiprocessing import cpu_count
from concurrent.futures import ThreadPoolExecutor
pathstr = str(path)
paths: list[str] = []
if path.is_dir():
# Build the full list of package-relative paths.
for basename, _dirnames, filenames in os.walk(path):
for filename in filenames:
fullname = os.path.join(basename, filename)
assert fullname.startswith(pathstr)
paths.append(fullname[len(pathstr) + 1:])
elif path.exists():
# Just return a single file entry if path is not a dir.
paths.append(pathstr)
def _get_file_info(filepath: str) -> tuple[str, DirectoryManifestFile]:
sha = hashlib.sha256()
fullfilepath = os.path.join(pathstr, filepath)
if not os.path.isfile(fullfilepath):
raise Exception(f'File not found: "{fullfilepath}"')
with open(fullfilepath, 'rb') as infile:
filebytes = infile.read()
filesize = len(filebytes)
sha.update(filebytes)
return (filepath,
DirectoryManifestFile(filehash=sha.hexdigest(),
filesize=filesize))
# Now use all procs to hash the files efficiently.
with ThreadPoolExecutor(max_workers=cpu_count()) as executor:
return cls(files=dict(executor.map(_get_file_info, paths)))
@classmethod
def get_empty_hash(cls) -> str:
"""Return the hash for an empty file."""
if cls._empty_hash is None:
import hashlib
sha = hashlib.sha256()
cls._empty_hash = sha.hexdigest()
return cls._empty_hash

View File

@ -38,8 +38,8 @@ class PipRequirement:
# installing it. And as far as manually-installed bits, pip itself must
# have some way to allow for that, right?...
PIP_REQUIREMENTS = [
PipRequirement(modulename='pylint', minversion=[2, 13, 9]),
PipRequirement(modulename='mypy', minversion=[0, 960]),
PipRequirement(modulename='pylint', minversion=[2, 14, 2]),
PipRequirement(modulename='mypy', minversion=[0, 961]),
PipRequirement(modulename='yapf', minversion=[0, 32, 0]),
PipRequirement(modulename='cpplint', minversion=[1, 6, 0]),
PipRequirement(modulename='pytest', minversion=[7, 1, 2]),
@ -53,8 +53,8 @@ PIP_REQUIREMENTS = [
PipRequirement(pipname='types-requests', minversion=[2, 27, 29]),
PipRequirement(pipname='types-pytz', minversion=[2021, 3, 8]),
PipRequirement(pipname='types-PyYAML', minversion=[6, 0, 7]),
PipRequirement(pipname='certifi', minversion=[2022, 5, 18, 1]),
PipRequirement(pipname='types-certifi', minversion=[2021, 10, 8, 2]),
PipRequirement(pipname='certifi', minversion=[2022, 6, 15]),
PipRequirement(pipname='types-certifi', minversion=[2021, 10, 8, 3]),
]
# Parts of full-tests suite we only run on particular days.

View File

@ -15,7 +15,7 @@ from efro.message._message import (Message, Response, EmptyResponse,
ErrorResponse, UnregisteredMessageIDError)
if TYPE_CHECKING:
from typing import Any, Callable, Optional, Union
from typing import Any, Callable, Optional, Union, Awaitable
from efro.message._protocol import MessageProtocol
@ -55,6 +55,11 @@ class MessageReceiver:
self._encode_filter_call: Optional[Callable[[Any, Response, dict],
None]] = None
# TODO: don't currently have async encode equivalent
# or either for sender; can add as needed.
self._decode_filter_async_call: Optional[Callable[
[Any, dict, Message], Awaitable[None]]] = None
# noinspection PyProtectedMember
def register_handler(
self, call: Callable[[Any, Message], Optional[Response]]) -> None:
@ -152,12 +157,26 @@ class MessageReceiver:
"""Function decorator for defining a decode filter.
Decode filters can be used to extract extra data from incoming
message dicts.
message dicts. This version will work for both handle_raw_message()
and handle_raw_message_async()
"""
assert self._decode_filter_call is None
self._decode_filter_call = call
return call
def decode_filter_async_method(
self, call: Callable[[Any, dict, Message], Awaitable[None]]
) -> Callable[[Any, dict, Message], Awaitable[None]]:
"""Function decorator for defining a decode filter.
Decode filters can be used to extract extra data from incoming
message dicts. Note that this version will only work with
handle_raw_message_async().
"""
assert self._decode_filter_async_call is None
self._decode_filter_async_call = call
return call
def encode_filter_method(
self, call: Callable[[Any, Response, dict], None]
) -> Callable[[Any, Response, dict], None]:
@ -183,8 +202,9 @@ class MessageReceiver:
else:
raise TypeError(msg)
def _decode_incoming_message(self, bound_obj: Any,
msg: str) -> tuple[Message, type[Message]]:
def _decode_incoming_message_base(
self, bound_obj: Any,
msg: str) -> tuple[Any, dict, Message, type[Message]]:
# Decode the incoming message.
msg_dict = self.protocol.decode_dict(msg)
msg_decoded = self.protocol.message_from_dict(msg_dict)
@ -192,7 +212,27 @@ class MessageReceiver:
assert issubclass(msgtype, Message)
if self._decode_filter_call is not None:
self._decode_filter_call(bound_obj, msg_dict, msg_decoded)
return bound_obj, msg_dict, msg_decoded, msgtype
def _decode_incoming_message(self, bound_obj: Any,
msg: str) -> tuple[Message, type[Message]]:
bound_obj, _msg_dict, msg_decoded, msgtype = (
self._decode_incoming_message_base(bound_obj=bound_obj, msg=msg))
# If they've set an async filter but are calling sync
# handle_raw_message() its likely a bug.
assert self._decode_filter_async_call is None
return msg_decoded, msgtype
async def _decode_incoming_message_async(
self, bound_obj: Any, msg: str) -> tuple[Message, type[Message]]:
bound_obj, msg_dict, msg_decoded, msgtype = (
self._decode_incoming_message_base(bound_obj=bound_obj, msg=msg))
if self._decode_filter_async_call is not None:
await self._decode_filter_async_call(bound_obj, msg_dict,
msg_decoded)
return msg_decoded, msgtype
def encode_user_response(self, bound_obj: Any,
@ -260,7 +300,7 @@ class MessageReceiver:
"""
assert self.is_async, "can't call async handler on sync receiver"
try:
msg_decoded, msgtype = self._decode_incoming_message(
msg_decoded, msgtype = await self._decode_incoming_message_async(
bound_obj, msg)
handler = self._handlers.get(msgtype)
if handler is None:

View File

@ -481,10 +481,9 @@ def _filter_tool_config(cfg: str) -> str:
pypaths = getconfig(PROJROOT).get('python_paths')
if pypaths is None:
raise RuntimeError('python_paths not set in project config')
cstr = "init-hook='import sys;"
cstr = 'init-hook=import sys;'
for path in pypaths:
cstr += f" sys.path.append('{PROJROOT}/{path}');"
cstr += "'"
cfg = cfg.replace(pylint_init_tag, cstr)
return cfg