mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-01-26 17:03:14 +08:00
prepping for workspaces
This commit is contained in:
parent
ff37609b83
commit
670bd9d4fd
@ -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"
|
||||
}
|
||||
8
.idea/dictionaries/ericf.xml
generated
8
.idea/dictionaries/ericf.xml
generated
@ -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>
|
||||
|
||||
@ -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!)
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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 \
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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:
|
||||
...
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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.
|
||||
|
||||
216
tools/bacloud
216
tools/bacloud
@ -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()
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
78
tools/bacommon/transfer.py
Normal file
78
tools/bacommon/transfer.py
Normal 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
|
||||
@ -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.
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user