subsystems cleanup

This commit is contained in:
Eric 2023-06-26 13:16:38 -07:00
parent 1e6b59d586
commit c13b937c79
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
22 changed files with 339 additions and 280 deletions

150
.efrocachemap generated
View File

@ -421,7 +421,7 @@
"build/assets/ba_data/audio/zoeOw.ogg": "https://files.ballistica.net/cache/ba1/74/be/fe45a8417e95b6a2233c51992a26", "build/assets/ba_data/audio/zoeOw.ogg": "https://files.ballistica.net/cache/ba1/74/be/fe45a8417e95b6a2233c51992a26",
"build/assets/ba_data/audio/zoePickup01.ogg": "https://files.ballistica.net/cache/ba1/48/ab/8cddfcde36a750856f3f81dd20c8", "build/assets/ba_data/audio/zoePickup01.ogg": "https://files.ballistica.net/cache/ba1/48/ab/8cddfcde36a750856f3f81dd20c8",
"build/assets/ba_data/audio/zoeScream01.ogg": "https://files.ballistica.net/cache/ba1/2b/46/8aedfa8741090247f04eb9e6df55", "build/assets/ba_data/audio/zoeScream01.ogg": "https://files.ballistica.net/cache/ba1/2b/46/8aedfa8741090247f04eb9e6df55",
"build/assets/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/cf/c0/8614c5b5ccbd2d26d77a09726eb6", "build/assets/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/3c/43/4b7614cd5d9db1c57a77a64271f6",
"build/assets/ba_data/data/languages/arabic.json": "https://files.ballistica.net/cache/ba1/83/87/06fc7255ebf8a895ad37d2dfa13d", "build/assets/ba_data/data/languages/arabic.json": "https://files.ballistica.net/cache/ba1/83/87/06fc7255ebf8a895ad37d2dfa13d",
"build/assets/ba_data/data/languages/belarussian.json": "https://files.ballistica.net/cache/ba1/57/68/d03a19b9035cfae7cdc5377d889a", "build/assets/ba_data/data/languages/belarussian.json": "https://files.ballistica.net/cache/ba1/57/68/d03a19b9035cfae7cdc5377d889a",
"build/assets/ba_data/data/languages/chinese.json": "https://files.ballistica.net/cache/ba1/25/f1/d88c72fbeeb121340244bc935df4", "build/assets/ba_data/data/languages/chinese.json": "https://files.ballistica.net/cache/ba1/25/f1/d88c72fbeeb121340244bc935df4",
@ -946,11 +946,11 @@
"build/assets/ba_data/meshes/zoeUpperArm.bob": "https://files.ballistica.net/cache/ba1/a8/a8/81010ac1ee9ec5ca872d5c5e853a", "build/assets/ba_data/meshes/zoeUpperArm.bob": "https://files.ballistica.net/cache/ba1/a8/a8/81010ac1ee9ec5ca872d5c5e853a",
"build/assets/ba_data/meshes/zoeUpperLeg.bob": "https://files.ballistica.net/cache/ba1/95/b2/502f74c70f934927f67cd505c3ad", "build/assets/ba_data/meshes/zoeUpperLeg.bob": "https://files.ballistica.net/cache/ba1/95/b2/502f74c70f934927f67cd505c3ad",
"build/assets/ba_data/python-site-packages/_yaml/__init__.py": "https://files.ballistica.net/cache/ba1/b0/9d/1968d73a04d6cf20e4e79657a6e7", "build/assets/ba_data/python-site-packages/_yaml/__init__.py": "https://files.ballistica.net/cache/ba1/b0/9d/1968d73a04d6cf20e4e79657a6e7",
"build/assets/ba_data/python-site-packages/certifi/__init__.py": "https://files.ballistica.net/cache/ba1/00/29/e4b90bb9e91a381dcd3992ed5b8d", "build/assets/ba_data/python-site-packages/certifi/__init__.py": "https://files.ballistica.net/cache/ba1/63/37/efa17f5b457b793332df33904162",
"build/assets/ba_data/python-site-packages/certifi/__main__.py": "https://files.ballistica.net/cache/ba1/ef/02/e73f8581609df189a9f61aca365b", "build/assets/ba_data/python-site-packages/certifi/__main__.py": "https://files.ballistica.net/cache/ba1/ef/02/e73f8581609df189a9f61aca365b",
"build/assets/ba_data/python-site-packages/certifi/cacert.pem": "https://files.ballistica.net/cache/ba1/5a/0e/670d05eeca037e593b8c2bf6bff4", "build/assets/ba_data/python-site-packages/certifi/cacert.pem": "https://files.ballistica.net/cache/ba1/6a/c2/9a6bccca11cd2ed7e16e27dfccec",
"build/assets/ba_data/python-site-packages/certifi/core.py": "https://files.ballistica.net/cache/ba1/1b/50/5388f1475fabd1b60031f985271c", "build/assets/ba_data/python-site-packages/certifi/core.py": "https://files.ballistica.net/cache/ba1/1b/50/5388f1475fabd1b60031f985271c",
"build/assets/ba_data/python-site-packages/typing_extensions.py": "https://files.ballistica.net/cache/ba1/6f/99/83cb2f859c5d1680af80aa15716b", "build/assets/ba_data/python-site-packages/typing_extensions.py": "https://files.ballistica.net/cache/ba1/11/61/88cec81052958f4f98239d4bf3ca",
"build/assets/ba_data/python-site-packages/yaml/__init__.py": "https://files.ballistica.net/cache/ba1/55/7c/37ea8dbd4fa4d6dac97f399b6fdd", "build/assets/ba_data/python-site-packages/yaml/__init__.py": "https://files.ballistica.net/cache/ba1/55/7c/37ea8dbd4fa4d6dac97f399b6fdd",
"build/assets/ba_data/python-site-packages/yaml/composer.py": "https://files.ballistica.net/cache/ba1/ce/f8/71e1f5f99ba2a7c44941b70afb06", "build/assets/ba_data/python-site-packages/yaml/composer.py": "https://files.ballistica.net/cache/ba1/ce/f8/71e1f5f99ba2a7c44941b70afb06",
"build/assets/ba_data/python-site-packages/yaml/constructor.py": "https://files.ballistica.net/cache/ba1/8a/15/e361e34b79491c81553bb3534062", "build/assets/ba_data/python-site-packages/yaml/constructor.py": "https://files.ballistica.net/cache/ba1/8a/15/e361e34b79491c81553bb3534062",
@ -2598,20 +2598,20 @@
"build/assets/pylib-android/_pyio.py": "https://files.ballistica.net/cache/ba1/a6/e8/8d66fbca88b13213cdd2177390b8", "build/assets/pylib-android/_pyio.py": "https://files.ballistica.net/cache/ba1/a6/e8/8d66fbca88b13213cdd2177390b8",
"build/assets/pylib-android/_sitebuiltins.py": "https://files.ballistica.net/cache/ba1/8b/5e/3f6e73917962fa014ad2c4a55e61", "build/assets/pylib-android/_sitebuiltins.py": "https://files.ballistica.net/cache/ba1/8b/5e/3f6e73917962fa014ad2c4a55e61",
"build/assets/pylib-android/_strptime.py": "https://files.ballistica.net/cache/ba1/ff/69/9c3f7647db7621bb88c43cc282d3", "build/assets/pylib-android/_strptime.py": "https://files.ballistica.net/cache/ba1/ff/69/9c3f7647db7621bb88c43cc282d3",
"build/assets/pylib-android/_sysconfigdata__linux_aarch64-linux-android.py": "https://files.ballistica.net/cache/ba1/aa/10/553aeca80a3710aa8f89f7fc3ac1", "build/assets/pylib-android/_sysconfigdata__linux_aarch64-linux-android.py": "https://files.ballistica.net/cache/ba1/3d/04/00e723f4dad156512d4229270993",
"build/assets/pylib-android/_sysconfigdata__linux_arm-linux-androideabi.py": "https://files.ballistica.net/cache/ba1/ad/f1/41818ec9b47e366cd72361921dbc", "build/assets/pylib-android/_sysconfigdata__linux_arm-linux-androideabi.py": "https://files.ballistica.net/cache/ba1/50/6b/3106270f7f7ae33e195d1f5caa55",
"build/assets/pylib-android/_sysconfigdata__linux_i686-linux-android.py": "https://files.ballistica.net/cache/ba1/c2/5c/b3bfba5ffbaadf288ea860d400a5", "build/assets/pylib-android/_sysconfigdata__linux_i686-linux-android.py": "https://files.ballistica.net/cache/ba1/f3/8a/2b60f9cbf0524885e5c1c99164c1",
"build/assets/pylib-android/_sysconfigdata__linux_x86_64-linux-android.py": "https://files.ballistica.net/cache/ba1/5b/ee/5962b42890b6758cea433e67376d", "build/assets/pylib-android/_sysconfigdata__linux_x86_64-linux-android.py": "https://files.ballistica.net/cache/ba1/d6/7f/056bcaf3d491d68254c206067d19",
"build/assets/pylib-android/_sysconfigdata_d_linux_aarch64-linux-android.py": "https://files.ballistica.net/cache/ba1/7b/ee/c8b9c2e67865226a6a1fbe5d244f", "build/assets/pylib-android/_sysconfigdata_d_linux_aarch64-linux-android.py": "https://files.ballistica.net/cache/ba1/e4/32/2948686ce1d4b37a1a3f8000e2dc",
"build/assets/pylib-android/_sysconfigdata_d_linux_arm-linux-androideabi.py": "https://files.ballistica.net/cache/ba1/37/a5/4b085a7f6cdfa03b3c6c887979ba", "build/assets/pylib-android/_sysconfigdata_d_linux_arm-linux-androideabi.py": "https://files.ballistica.net/cache/ba1/a4/d1/4baa8cfc0019363742349449942b",
"build/assets/pylib-android/_sysconfigdata_d_linux_i686-linux-android.py": "https://files.ballistica.net/cache/ba1/6f/ec/8a10cf682d5bf05bb6a7ab811d08", "build/assets/pylib-android/_sysconfigdata_d_linux_i686-linux-android.py": "https://files.ballistica.net/cache/ba1/2a/d6/c96209fd69a4eef4a7d730cd16e8",
"build/assets/pylib-android/_sysconfigdata_d_linux_x86_64-linux-android.py": "https://files.ballistica.net/cache/ba1/81/1d/8202416f4ba308e6a011139115e6", "build/assets/pylib-android/_sysconfigdata_d_linux_x86_64-linux-android.py": "https://files.ballistica.net/cache/ba1/bf/6c/e8f2788661352014e973351f35cf",
"build/assets/pylib-android/_threading_local.py": "https://files.ballistica.net/cache/ba1/4a/96/88e3987d7d692db46feb9214945e", "build/assets/pylib-android/_threading_local.py": "https://files.ballistica.net/cache/ba1/4a/96/88e3987d7d692db46feb9214945e",
"build/assets/pylib-android/_weakrefset.py": "https://files.ballistica.net/cache/ba1/e4/fa/8532ace46dfbc35149c41ea497f7", "build/assets/pylib-android/_weakrefset.py": "https://files.ballistica.net/cache/ba1/e4/fa/8532ace46dfbc35149c41ea497f7",
"build/assets/pylib-android/abc.py": "https://files.ballistica.net/cache/ba1/a9/66/1fcfe6c12211ec1b16391247947c", "build/assets/pylib-android/abc.py": "https://files.ballistica.net/cache/ba1/a0/da/a1ed187eee8690c1e8438b97da90",
"build/assets/pylib-android/aifc.py": "https://files.ballistica.net/cache/ba1/1b/91/34c72b1e542417bee5bf345a1d0a", "build/assets/pylib-android/aifc.py": "https://files.ballistica.net/cache/ba1/1b/91/34c72b1e542417bee5bf345a1d0a",
"build/assets/pylib-android/antigravity.py": "https://files.ballistica.net/cache/ba1/6d/56/bedf73be574cb6d7117caf5d334c", "build/assets/pylib-android/antigravity.py": "https://files.ballistica.net/cache/ba1/6d/56/bedf73be574cb6d7117caf5d334c",
"build/assets/pylib-android/argparse.py": "https://files.ballistica.net/cache/ba1/2d/c7/f53a0390532cfabb416e25e72035", "build/assets/pylib-android/argparse.py": "https://files.ballistica.net/cache/ba1/e2/2c/ac9b12c09592929d57eb982fc554",
"build/assets/pylib-android/ast.py": "https://files.ballistica.net/cache/ba1/3a/aa/1b0e56b21b28155707c54bc225a8", "build/assets/pylib-android/ast.py": "https://files.ballistica.net/cache/ba1/3a/aa/1b0e56b21b28155707c54bc225a8",
"build/assets/pylib-android/asynchat.py": "https://files.ballistica.net/cache/ba1/2e/f3/a0ce322332fabbf8fad4e133c6a3", "build/assets/pylib-android/asynchat.py": "https://files.ballistica.net/cache/ba1/2e/f3/a0ce322332fabbf8fad4e133c6a3",
"build/assets/pylib-android/asyncio/__init__.py": "https://files.ballistica.net/cache/ba1/ed/f0/e79e2b8b85c08f09fd14668e4822", "build/assets/pylib-android/asyncio/__init__.py": "https://files.ballistica.net/cache/ba1/ed/f0/e79e2b8b85c08f09fd14668e4822",
@ -2633,26 +2633,26 @@
"build/assets/pylib-android/asyncio/protocols.py": "https://files.ballistica.net/cache/ba1/b8/aa/105b79d24f88c7a2c2cdbc8e7814", "build/assets/pylib-android/asyncio/protocols.py": "https://files.ballistica.net/cache/ba1/b8/aa/105b79d24f88c7a2c2cdbc8e7814",
"build/assets/pylib-android/asyncio/queues.py": "https://files.ballistica.net/cache/ba1/f6/3b/e54780730992e2377c51ac373126", "build/assets/pylib-android/asyncio/queues.py": "https://files.ballistica.net/cache/ba1/f6/3b/e54780730992e2377c51ac373126",
"build/assets/pylib-android/asyncio/runners.py": "https://files.ballistica.net/cache/ba1/f1/84/754fba961e4ea6e77a9aec633afc", "build/assets/pylib-android/asyncio/runners.py": "https://files.ballistica.net/cache/ba1/f1/84/754fba961e4ea6e77a9aec633afc",
"build/assets/pylib-android/asyncio/selector_events.py": "https://files.ballistica.net/cache/ba1/f7/ee/7809d8b807ccda22db7870f508ac", "build/assets/pylib-android/asyncio/selector_events.py": "https://files.ballistica.net/cache/ba1/a1/08/fbd3a49f967da245f39cebf7694e",
"build/assets/pylib-android/asyncio/sslproto.py": "https://files.ballistica.net/cache/ba1/3a/63/9532c92e12c9bf06fd131a6fc419", "build/assets/pylib-android/asyncio/sslproto.py": "https://files.ballistica.net/cache/ba1/2e/c1/b21e523055147d94c8c634154aab",
"build/assets/pylib-android/asyncio/staggered.py": "https://files.ballistica.net/cache/ba1/f5/05/6f0a56b73b477a9fa65e71145366", "build/assets/pylib-android/asyncio/staggered.py": "https://files.ballistica.net/cache/ba1/f5/05/6f0a56b73b477a9fa65e71145366",
"build/assets/pylib-android/asyncio/streams.py": "https://files.ballistica.net/cache/ba1/e9/c6/09b92b4e9bd3383cf4004c6186c6", "build/assets/pylib-android/asyncio/streams.py": "https://files.ballistica.net/cache/ba1/e9/c6/09b92b4e9bd3383cf4004c6186c6",
"build/assets/pylib-android/asyncio/subprocess.py": "https://files.ballistica.net/cache/ba1/a5/ff/69004c02e2f47d0077a77803c06b", "build/assets/pylib-android/asyncio/subprocess.py": "https://files.ballistica.net/cache/ba1/ed/b8/d98278300b6c99f36cd08643c743",
"build/assets/pylib-android/asyncio/taskgroups.py": "https://files.ballistica.net/cache/ba1/51/62/e5b1806d9b647383d34ba1b21b56", "build/assets/pylib-android/asyncio/taskgroups.py": "https://files.ballistica.net/cache/ba1/51/62/e5b1806d9b647383d34ba1b21b56",
"build/assets/pylib-android/asyncio/tasks.py": "https://files.ballistica.net/cache/ba1/23/45/50593cd4928e6ee2c9591b6928ca", "build/assets/pylib-android/asyncio/tasks.py": "https://files.ballistica.net/cache/ba1/23/45/50593cd4928e6ee2c9591b6928ca",
"build/assets/pylib-android/asyncio/threads.py": "https://files.ballistica.net/cache/ba1/7b/bf/81d424901524510e07b5d20e4a50", "build/assets/pylib-android/asyncio/threads.py": "https://files.ballistica.net/cache/ba1/7b/bf/81d424901524510e07b5d20e4a50",
"build/assets/pylib-android/asyncio/timeouts.py": "https://files.ballistica.net/cache/ba1/c7/cb/81c7ee938bc47ff75342befc872a", "build/assets/pylib-android/asyncio/timeouts.py": "https://files.ballistica.net/cache/ba1/c7/cb/81c7ee938bc47ff75342befc872a",
"build/assets/pylib-android/asyncio/transports.py": "https://files.ballistica.net/cache/ba1/04/59/8090d813bb363cea9bf714b97c3f", "build/assets/pylib-android/asyncio/transports.py": "https://files.ballistica.net/cache/ba1/04/59/8090d813bb363cea9bf714b97c3f",
"build/assets/pylib-android/asyncio/trsock.py": "https://files.ballistica.net/cache/ba1/30/5d/016a93b3e5224cb45af43fdc52b9", "build/assets/pylib-android/asyncio/trsock.py": "https://files.ballistica.net/cache/ba1/30/5d/016a93b3e5224cb45af43fdc52b9",
"build/assets/pylib-android/asyncio/unix_events.py": "https://files.ballistica.net/cache/ba1/9c/e0/da3a12b544df9c10fd3b3e0b2f37", "build/assets/pylib-android/asyncio/unix_events.py": "https://files.ballistica.net/cache/ba1/cb/b9/68f7c1e4f87f23b187d7140839f1",
"build/assets/pylib-android/asyncio/windows_events.py": "https://files.ballistica.net/cache/ba1/95/46/37d8416216d656011178dbcedbb4", "build/assets/pylib-android/asyncio/windows_events.py": "https://files.ballistica.net/cache/ba1/95/46/37d8416216d656011178dbcedbb4",
"build/assets/pylib-android/asyncio/windows_utils.py": "https://files.ballistica.net/cache/ba1/4e/fb/ef16e6692c9424804d9bdc496761", "build/assets/pylib-android/asyncio/windows_utils.py": "https://files.ballistica.net/cache/ba1/4e/fb/ef16e6692c9424804d9bdc496761",
"build/assets/pylib-android/asyncore.py": "https://files.ballistica.net/cache/ba1/af/21/d495fa6ccc47b51ca8a8a382ea1c", "build/assets/pylib-android/asyncore.py": "https://files.ballistica.net/cache/ba1/af/21/d495fa6ccc47b51ca8a8a382ea1c",
"build/assets/pylib-android/base64.py": "https://files.ballistica.net/cache/ba1/5f/8b/bbfda4b9145b1968cb9badf8325b", "build/assets/pylib-android/base64.py": "https://files.ballistica.net/cache/ba1/5f/8b/bbfda4b9145b1968cb9badf8325b",
"build/assets/pylib-android/bdb.py": "https://files.ballistica.net/cache/ba1/ae/cf/5370ea25c5decdf8dfa07caf0bfc", "build/assets/pylib-android/bdb.py": "https://files.ballistica.net/cache/ba1/1e/26/c626405bfc06b1e7231183eb928a",
"build/assets/pylib-android/bisect.py": "https://files.ballistica.net/cache/ba1/9b/70/437e327d5176da41192567ad0064", "build/assets/pylib-android/bisect.py": "https://files.ballistica.net/cache/ba1/9b/70/437e327d5176da41192567ad0064",
"build/assets/pylib-android/bz2.py": "https://files.ballistica.net/cache/ba1/cd/6a/5f2491bc52afd8fc180097371473", "build/assets/pylib-android/bz2.py": "https://files.ballistica.net/cache/ba1/cd/6a/5f2491bc52afd8fc180097371473",
"build/assets/pylib-android/cProfile.py": "https://files.ballistica.net/cache/ba1/d5/ef/924a68c0f747ad727a6326ad8e5a", "build/assets/pylib-android/cProfile.py": "https://files.ballistica.net/cache/ba1/9e/9c/07ac3b9e4195a62b74e4f2b9489f",
"build/assets/pylib-android/calendar.py": "https://files.ballistica.net/cache/ba1/4e/f3/d6d85d44e36212e5d784051c80b6", "build/assets/pylib-android/calendar.py": "https://files.ballistica.net/cache/ba1/4e/f3/d6d85d44e36212e5d784051c80b6",
"build/assets/pylib-android/cgi.py": "https://files.ballistica.net/cache/ba1/09/0c/5cfc8b4b92a730beec975159bd2a", "build/assets/pylib-android/cgi.py": "https://files.ballistica.net/cache/ba1/09/0c/5cfc8b4b92a730beec975159bd2a",
"build/assets/pylib-android/cgitb.py": "https://files.ballistica.net/cache/ba1/2b/cf/f1cec7f3a3a9c96de7a55ebb4ea3", "build/assets/pylib-android/cgitb.py": "https://files.ballistica.net/cache/ba1/2b/cf/f1cec7f3a3a9c96de7a55ebb4ea3",
@ -2691,7 +2691,7 @@
"build/assets/pylib-android/curses/has_key.py": "https://files.ballistica.net/cache/ba1/c7/4b/8d6db329fbbd872b7b91bfa94624", "build/assets/pylib-android/curses/has_key.py": "https://files.ballistica.net/cache/ba1/c7/4b/8d6db329fbbd872b7b91bfa94624",
"build/assets/pylib-android/curses/panel.py": "https://files.ballistica.net/cache/ba1/8f/36/fdade9588f8a4362d2cc057a6eff", "build/assets/pylib-android/curses/panel.py": "https://files.ballistica.net/cache/ba1/8f/36/fdade9588f8a4362d2cc057a6eff",
"build/assets/pylib-android/curses/textpad.py": "https://files.ballistica.net/cache/ba1/94/aa/9ebc47a6068d4461652346646dbb", "build/assets/pylib-android/curses/textpad.py": "https://files.ballistica.net/cache/ba1/94/aa/9ebc47a6068d4461652346646dbb",
"build/assets/pylib-android/dataclasses.py": "https://files.ballistica.net/cache/ba1/13/28/e12f8255b856ced026e75a623671", "build/assets/pylib-android/dataclasses.py": "https://files.ballistica.net/cache/ba1/fe/be/ea138bff21dbed88762be772514e",
"build/assets/pylib-android/datetime.py": "https://files.ballistica.net/cache/ba1/5d/cf/d7f3b1a4db8214c1442164ac999c", "build/assets/pylib-android/datetime.py": "https://files.ballistica.net/cache/ba1/5d/cf/d7f3b1a4db8214c1442164ac999c",
"build/assets/pylib-android/decimal.py": "https://files.ballistica.net/cache/ba1/f5/7d/255d45b5d1d7d8e13c41a283c3e4", "build/assets/pylib-android/decimal.py": "https://files.ballistica.net/cache/ba1/f5/7d/255d45b5d1d7d8e13c41a283c3e4",
"build/assets/pylib-android/difflib.py": "https://files.ballistica.net/cache/ba1/6b/3c/8fd541b2b8d0320727025cd25275", "build/assets/pylib-android/difflib.py": "https://files.ballistica.net/cache/ba1/6b/3c/8fd541b2b8d0320727025cd25275",
@ -2848,7 +2848,7 @@
"build/assets/pylib-android/encodings/utf_8_sig.py": "https://files.ballistica.net/cache/ba1/8f/35/42863ef311d8b970a37c0d66b0de", "build/assets/pylib-android/encodings/utf_8_sig.py": "https://files.ballistica.net/cache/ba1/8f/35/42863ef311d8b970a37c0d66b0de",
"build/assets/pylib-android/encodings/uu_codec.py": "https://files.ballistica.net/cache/ba1/4e/f8/a65413574c017a96b97fc1638ba6", "build/assets/pylib-android/encodings/uu_codec.py": "https://files.ballistica.net/cache/ba1/4e/f8/a65413574c017a96b97fc1638ba6",
"build/assets/pylib-android/encodings/zlib_codec.py": "https://files.ballistica.net/cache/ba1/13/88/fb103fdf395451bfc8a2d60933a9", "build/assets/pylib-android/encodings/zlib_codec.py": "https://files.ballistica.net/cache/ba1/13/88/fb103fdf395451bfc8a2d60933a9",
"build/assets/pylib-android/enum.py": "https://files.ballistica.net/cache/ba1/93/e4/c39acfbbebdc9c4f3d204f17e496", "build/assets/pylib-android/enum.py": "https://files.ballistica.net/cache/ba1/62/a3/41268ae3fef3c4affc625c2e68d2",
"build/assets/pylib-android/filecmp.py": "https://files.ballistica.net/cache/ba1/76/48/fdc6d0fc8bae7429d5e4081cf353", "build/assets/pylib-android/filecmp.py": "https://files.ballistica.net/cache/ba1/76/48/fdc6d0fc8bae7429d5e4081cf353",
"build/assets/pylib-android/fileinput.py": "https://files.ballistica.net/cache/ba1/c3/de/f1041e6b12dd5f1906c9dbbd1101", "build/assets/pylib-android/fileinput.py": "https://files.ballistica.net/cache/ba1/c3/de/f1041e6b12dd5f1906c9dbbd1101",
"build/assets/pylib-android/fnmatch.py": "https://files.ballistica.net/cache/ba1/a1/bc/67633695d4defd4c0886428c5363", "build/assets/pylib-android/fnmatch.py": "https://files.ballistica.net/cache/ba1/a1/bc/67633695d4defd4c0886428c5363",
@ -2869,15 +2869,15 @@
"build/assets/pylib-android/html/entities.py": "https://files.ballistica.net/cache/ba1/85/61/b405414160a08f613bdfd89e161b", "build/assets/pylib-android/html/entities.py": "https://files.ballistica.net/cache/ba1/85/61/b405414160a08f613bdfd89e161b",
"build/assets/pylib-android/html/parser.py": "https://files.ballistica.net/cache/ba1/c2/ff/6801a3ef6a2863d37b899615efb8", "build/assets/pylib-android/html/parser.py": "https://files.ballistica.net/cache/ba1/c2/ff/6801a3ef6a2863d37b899615efb8",
"build/assets/pylib-android/http/__init__.py": "https://files.ballistica.net/cache/ba1/1d/ab/578aab89ead7171ef10638e88a5d", "build/assets/pylib-android/http/__init__.py": "https://files.ballistica.net/cache/ba1/1d/ab/578aab89ead7171ef10638e88a5d",
"build/assets/pylib-android/http/client.py": "https://files.ballistica.net/cache/ba1/d0/b3/7013d5f9794a8311c3545ee5416d", "build/assets/pylib-android/http/client.py": "https://files.ballistica.net/cache/ba1/a3/f2/cd21f8ce7a4ee1b47f0b19fb57f7",
"build/assets/pylib-android/http/cookiejar.py": "https://files.ballistica.net/cache/ba1/f3/24/a21e7c17f40e4d5c78139875811c", "build/assets/pylib-android/http/cookiejar.py": "https://files.ballistica.net/cache/ba1/f3/24/a21e7c17f40e4d5c78139875811c",
"build/assets/pylib-android/http/cookies.py": "https://files.ballistica.net/cache/ba1/93/19/b95b505a644b9a112b4c7a7678c3", "build/assets/pylib-android/http/cookies.py": "https://files.ballistica.net/cache/ba1/93/19/b95b505a644b9a112b4c7a7678c3",
"build/assets/pylib-android/http/server.py": "https://files.ballistica.net/cache/ba1/a0/6b/26669fc0eb0ef81b819fe8a6602d", "build/assets/pylib-android/http/server.py": "https://files.ballistica.net/cache/ba1/4b/28/6e2b5e6112af17e097539eadd9d7",
"build/assets/pylib-android/imghdr.py": "https://files.ballistica.net/cache/ba1/01/1d/27a98acf3774279a584525614ba3", "build/assets/pylib-android/imghdr.py": "https://files.ballistica.net/cache/ba1/01/1d/27a98acf3774279a584525614ba3",
"build/assets/pylib-android/imp.py": "https://files.ballistica.net/cache/ba1/34/bd/1d1620eb0d42f9ef9bbbb11cafae", "build/assets/pylib-android/imp.py": "https://files.ballistica.net/cache/ba1/34/bd/1d1620eb0d42f9ef9bbbb11cafae",
"build/assets/pylib-android/importlib/__init__.py": "https://files.ballistica.net/cache/ba1/2f/6c/1fb453d61417245cd196311a9b52", "build/assets/pylib-android/importlib/__init__.py": "https://files.ballistica.net/cache/ba1/2f/6c/1fb453d61417245cd196311a9b52",
"build/assets/pylib-android/importlib/_abc.py": "https://files.ballistica.net/cache/ba1/72/0f/8056e5cd852156378b532ddbbb7c", "build/assets/pylib-android/importlib/_abc.py": "https://files.ballistica.net/cache/ba1/72/0f/8056e5cd852156378b532ddbbb7c",
"build/assets/pylib-android/importlib/_bootstrap.py": "https://files.ballistica.net/cache/ba1/da/9d/e7a6d6839a7185147f35f2040de7", "build/assets/pylib-android/importlib/_bootstrap.py": "https://files.ballistica.net/cache/ba1/6f/48/19e9443e3fda3c28c62f15a8238c",
"build/assets/pylib-android/importlib/_bootstrap_external.py": "https://files.ballistica.net/cache/ba1/aa/2d/5aab23ea30219b7a42d2b7d27201", "build/assets/pylib-android/importlib/_bootstrap_external.py": "https://files.ballistica.net/cache/ba1/aa/2d/5aab23ea30219b7a42d2b7d27201",
"build/assets/pylib-android/importlib/abc.py": "https://files.ballistica.net/cache/ba1/c9/69/7e928ff17089dc6aea96f5b33003", "build/assets/pylib-android/importlib/abc.py": "https://files.ballistica.net/cache/ba1/c9/69/7e928ff17089dc6aea96f5b33003",
"build/assets/pylib-android/importlib/machinery.py": "https://files.ballistica.net/cache/ba1/6a/05/6c561b4ea8eb5457761c8a9fd704", "build/assets/pylib-android/importlib/machinery.py": "https://files.ballistica.net/cache/ba1/6a/05/6c561b4ea8eb5457761c8a9fd704",
@ -2899,7 +2899,7 @@
"build/assets/pylib-android/importlib/resources/simple.py": "https://files.ballistica.net/cache/ba1/ae/95/788edf31cbfffd53db748d76f0b9", "build/assets/pylib-android/importlib/resources/simple.py": "https://files.ballistica.net/cache/ba1/ae/95/788edf31cbfffd53db748d76f0b9",
"build/assets/pylib-android/importlib/simple.py": "https://files.ballistica.net/cache/ba1/f3/4f/28cd359ecaf9fd6cf18a4dca2c33", "build/assets/pylib-android/importlib/simple.py": "https://files.ballistica.net/cache/ba1/f3/4f/28cd359ecaf9fd6cf18a4dca2c33",
"build/assets/pylib-android/importlib/util.py": "https://files.ballistica.net/cache/ba1/09/c3/a96d1265db211f32b73a5f76e1ac", "build/assets/pylib-android/importlib/util.py": "https://files.ballistica.net/cache/ba1/09/c3/a96d1265db211f32b73a5f76e1ac",
"build/assets/pylib-android/inspect.py": "https://files.ballistica.net/cache/ba1/5a/32/1fd388139d2beff15f4306b1fb95", "build/assets/pylib-android/inspect.py": "https://files.ballistica.net/cache/ba1/36/5d/413e26b3a77118e51987cfd2eb1f",
"build/assets/pylib-android/io.py": "https://files.ballistica.net/cache/ba1/4a/3a/b68b3f38d3dcf9282f4839fe5d84", "build/assets/pylib-android/io.py": "https://files.ballistica.net/cache/ba1/4a/3a/b68b3f38d3dcf9282f4839fe5d84",
"build/assets/pylib-android/ipaddress.py": "https://files.ballistica.net/cache/ba1/b9/83/1cf9058885b35a2411b4cbada972", "build/assets/pylib-android/ipaddress.py": "https://files.ballistica.net/cache/ba1/b9/83/1cf9058885b35a2411b4cbada972",
"build/assets/pylib-android/json/__init__.py": "https://files.ballistica.net/cache/ba1/e8/b0/00d2bf8c53b55a72bc3053800596", "build/assets/pylib-android/json/__init__.py": "https://files.ballistica.net/cache/ba1/e8/b0/00d2bf8c53b55a72bc3053800596",
@ -2911,7 +2911,7 @@
"build/assets/pylib-android/linecache.py": "https://files.ballistica.net/cache/ba1/27/12/c31a3a6d7625d437029a6b7470c9", "build/assets/pylib-android/linecache.py": "https://files.ballistica.net/cache/ba1/27/12/c31a3a6d7625d437029a6b7470c9",
"build/assets/pylib-android/locale.py": "https://files.ballistica.net/cache/ba1/42/f3/f48fed6a16da350de46aa85b446a", "build/assets/pylib-android/locale.py": "https://files.ballistica.net/cache/ba1/42/f3/f48fed6a16da350de46aa85b446a",
"build/assets/pylib-android/logging/__init__.py": "https://files.ballistica.net/cache/ba1/6e/5d/6f80af880857f5eb29b038512c6d", "build/assets/pylib-android/logging/__init__.py": "https://files.ballistica.net/cache/ba1/6e/5d/6f80af880857f5eb29b038512c6d",
"build/assets/pylib-android/logging/config.py": "https://files.ballistica.net/cache/ba1/ff/06/97c60f90420c32c697a30bfdfe90", "build/assets/pylib-android/logging/config.py": "https://files.ballistica.net/cache/ba1/f8/11/372496ec5ae34e8047f0280686be",
"build/assets/pylib-android/logging/handlers.py": "https://files.ballistica.net/cache/ba1/ab/38/81444783ab84f3859e022b356989", "build/assets/pylib-android/logging/handlers.py": "https://files.ballistica.net/cache/ba1/ab/38/81444783ab84f3859e022b356989",
"build/assets/pylib-android/lzma.py": "https://files.ballistica.net/cache/ba1/d7/38/8640aa6af4c64fdc821471930f57", "build/assets/pylib-android/lzma.py": "https://files.ballistica.net/cache/ba1/d7/38/8640aa6af4c64fdc821471930f57",
"build/assets/pylib-android/mailbox.py": "https://files.ballistica.net/cache/ba1/d9/db/3c7510e38484ade3d88446b8f231", "build/assets/pylib-android/mailbox.py": "https://files.ballistica.net/cache/ba1/d9/db/3c7510e38484ade3d88446b8f231",
@ -2920,27 +2920,27 @@
"build/assets/pylib-android/modulefinder.py": "https://files.ballistica.net/cache/ba1/3f/c0/74c018de1dd15ab2e309be199dea", "build/assets/pylib-android/modulefinder.py": "https://files.ballistica.net/cache/ba1/3f/c0/74c018de1dd15ab2e309be199dea",
"build/assets/pylib-android/netrc.py": "https://files.ballistica.net/cache/ba1/9a/86/9ca83b316ab5a19a0a2cf22efaf3", "build/assets/pylib-android/netrc.py": "https://files.ballistica.net/cache/ba1/9a/86/9ca83b316ab5a19a0a2cf22efaf3",
"build/assets/pylib-android/nntplib.py": "https://files.ballistica.net/cache/ba1/49/26/aa991bd4b96b9318d6242135abf9", "build/assets/pylib-android/nntplib.py": "https://files.ballistica.net/cache/ba1/49/26/aa991bd4b96b9318d6242135abf9",
"build/assets/pylib-android/ntpath.py": "https://files.ballistica.net/cache/ba1/36/55/7dc9fdcd9eeeab4d46c1a364bd5a", "build/assets/pylib-android/ntpath.py": "https://files.ballistica.net/cache/ba1/57/ea/cdf2824f306ae2b03d5818294c94",
"build/assets/pylib-android/nturl2path.py": "https://files.ballistica.net/cache/ba1/93/76/24c4b6f213b652addea66296ccc4", "build/assets/pylib-android/nturl2path.py": "https://files.ballistica.net/cache/ba1/93/76/24c4b6f213b652addea66296ccc4",
"build/assets/pylib-android/numbers.py": "https://files.ballistica.net/cache/ba1/0c/dc/b42317422500d514ec67d496a06e", "build/assets/pylib-android/numbers.py": "https://files.ballistica.net/cache/ba1/0c/dc/b42317422500d514ec67d496a06e",
"build/assets/pylib-android/opcode.py": "https://files.ballistica.net/cache/ba1/18/cb/a8b1d09ec6fcccfe20e5b13db980", "build/assets/pylib-android/opcode.py": "https://files.ballistica.net/cache/ba1/18/cb/a8b1d09ec6fcccfe20e5b13db980",
"build/assets/pylib-android/operator.py": "https://files.ballistica.net/cache/ba1/61/e1/97bc43df97ec39ae3e5e59b11c19", "build/assets/pylib-android/operator.py": "https://files.ballistica.net/cache/ba1/61/e1/97bc43df97ec39ae3e5e59b11c19",
"build/assets/pylib-android/optparse.py": "https://files.ballistica.net/cache/ba1/5f/65/f891612b68c71a2846da86254285", "build/assets/pylib-android/optparse.py": "https://files.ballistica.net/cache/ba1/5f/65/f891612b68c71a2846da86254285",
"build/assets/pylib-android/os.py": "https://files.ballistica.net/cache/ba1/36/f9/692131ffb9ba4db510de31afc651", "build/assets/pylib-android/os.py": "https://files.ballistica.net/cache/ba1/36/f9/692131ffb9ba4db510de31afc651",
"build/assets/pylib-android/pathlib.py": "https://files.ballistica.net/cache/ba1/3e/8b/893cbe95729cdb6d0cc796e89e19", "build/assets/pylib-android/pathlib.py": "https://files.ballistica.net/cache/ba1/09/5e/c821fec243124d0a286b4de3848a",
"build/assets/pylib-android/pdb.py": "https://files.ballistica.net/cache/ba1/da/4c/484348d4b2009479dadd878f20b0", "build/assets/pylib-android/pdb.py": "https://files.ballistica.net/cache/ba1/11/7b/0d24ccb89edc5f183c94f6722f70",
"build/assets/pylib-android/pickle.py": "https://files.ballistica.net/cache/ba1/e6/f9/f53d29988454690ccde3279c7c38", "build/assets/pylib-android/pickle.py": "https://files.ballistica.net/cache/ba1/e6/f9/f53d29988454690ccde3279c7c38",
"build/assets/pylib-android/pickletools.py": "https://files.ballistica.net/cache/ba1/85/b3/0fba86d32dfc4a588300dedf5f01", "build/assets/pylib-android/pickletools.py": "https://files.ballistica.net/cache/ba1/85/b3/0fba86d32dfc4a588300dedf5f01",
"build/assets/pylib-android/pipes.py": "https://files.ballistica.net/cache/ba1/2d/d7/96bdbb87982034234fec50d4526c", "build/assets/pylib-android/pipes.py": "https://files.ballistica.net/cache/ba1/2d/d7/96bdbb87982034234fec50d4526c",
"build/assets/pylib-android/pkgutil.py": "https://files.ballistica.net/cache/ba1/37/2a/540a2f2f3a324d5ff8e137579aad", "build/assets/pylib-android/pkgutil.py": "https://files.ballistica.net/cache/ba1/8e/3f/dfcb6c90dc50245aee89c49eb515",
"build/assets/pylib-android/platform.py": "https://files.ballistica.net/cache/ba1/07/2a/4f8d700c73429d420ca8f5f3a90d", "build/assets/pylib-android/platform.py": "https://files.ballistica.net/cache/ba1/07/2a/4f8d700c73429d420ca8f5f3a90d",
"build/assets/pylib-android/plistlib.py": "https://files.ballistica.net/cache/ba1/8a/a9/836b7a69628873b25811c4d5fffa", "build/assets/pylib-android/plistlib.py": "https://files.ballistica.net/cache/ba1/8a/a9/836b7a69628873b25811c4d5fffa",
"build/assets/pylib-android/poplib.py": "https://files.ballistica.net/cache/ba1/82/9d/c4df5d8e082ad414d499ec4678bd", "build/assets/pylib-android/poplib.py": "https://files.ballistica.net/cache/ba1/82/9d/c4df5d8e082ad414d499ec4678bd",
"build/assets/pylib-android/posixpath.py": "https://files.ballistica.net/cache/ba1/2a/6b/3f940f89b0819ee002b6b82b0318", "build/assets/pylib-android/posixpath.py": "https://files.ballistica.net/cache/ba1/2a/6b/3f940f89b0819ee002b6b82b0318",
"build/assets/pylib-android/pprint.py": "https://files.ballistica.net/cache/ba1/b4/e9/efe35d05b6126d41da36eda47754", "build/assets/pylib-android/pprint.py": "https://files.ballistica.net/cache/ba1/b4/e9/efe35d05b6126d41da36eda47754",
"build/assets/pylib-android/profile.py": "https://files.ballistica.net/cache/ba1/4c/08/16d9278d2a33e28835fcd291fb62", "build/assets/pylib-android/profile.py": "https://files.ballistica.net/cache/ba1/a9/f1/1f8aa6a2905706a5fc9273d6c981",
"build/assets/pylib-android/pstats.py": "https://files.ballistica.net/cache/ba1/c5/88/4aaae199265ab0c3849d053bc996", "build/assets/pylib-android/pstats.py": "https://files.ballistica.net/cache/ba1/c5/88/4aaae199265ab0c3849d053bc996",
"build/assets/pylib-android/pty.py": "https://files.ballistica.net/cache/ba1/1d/e2/d1bfbdc54a770de7b3010d322620", "build/assets/pylib-android/pty.py": "https://files.ballistica.net/cache/ba1/c9/7b/51147360cf7fd92c364ab8068a2b",
"build/assets/pylib-android/py_compile.py": "https://files.ballistica.net/cache/ba1/77/2b/429c589d2e005e8bd79b12f55a92", "build/assets/pylib-android/py_compile.py": "https://files.ballistica.net/cache/ba1/77/2b/429c589d2e005e8bd79b12f55a92",
"build/assets/pylib-android/pyclbr.py": "https://files.ballistica.net/cache/ba1/bf/19/89a2c73108005e874a640760476a", "build/assets/pylib-android/pyclbr.py": "https://files.ballistica.net/cache/ba1/bf/19/89a2c73108005e874a640760476a",
"build/assets/pylib-android/pydoc.py": "https://files.ballistica.net/cache/ba1/0f/8a/4b2f4a9d23e429c01660a4290bd8", "build/assets/pylib-android/pydoc.py": "https://files.ballistica.net/cache/ba1/0f/8a/4b2f4a9d23e429c01660a4290bd8",
@ -2960,7 +2960,7 @@
"build/assets/pylib-android/selectors.py": "https://files.ballistica.net/cache/ba1/98/e0/d83849452cbc2cc1381555bd5024", "build/assets/pylib-android/selectors.py": "https://files.ballistica.net/cache/ba1/98/e0/d83849452cbc2cc1381555bd5024",
"build/assets/pylib-android/shelve.py": "https://files.ballistica.net/cache/ba1/3e/56/9c07c863ecbd7f35a6c382d1785a", "build/assets/pylib-android/shelve.py": "https://files.ballistica.net/cache/ba1/3e/56/9c07c863ecbd7f35a6c382d1785a",
"build/assets/pylib-android/shlex.py": "https://files.ballistica.net/cache/ba1/08/73/fac90a491702950816ead0e59dd0", "build/assets/pylib-android/shlex.py": "https://files.ballistica.net/cache/ba1/08/73/fac90a491702950816ead0e59dd0",
"build/assets/pylib-android/shutil.py": "https://files.ballistica.net/cache/ba1/65/1b/e1edc2110d483c96fbe26f40665d", "build/assets/pylib-android/shutil.py": "https://files.ballistica.net/cache/ba1/ed/25/b38eacc66c9059651e6666ec1869",
"build/assets/pylib-android/signal.py": "https://files.ballistica.net/cache/ba1/11/4e/f47b1798fca6f56ac8a250974b3e", "build/assets/pylib-android/signal.py": "https://files.ballistica.net/cache/ba1/11/4e/f47b1798fca6f56ac8a250974b3e",
"build/assets/pylib-android/site.py": "https://files.ballistica.net/cache/ba1/2a/99/f7de2702aa8411d35acbb91fe926", "build/assets/pylib-android/site.py": "https://files.ballistica.net/cache/ba1/2a/99/f7de2702aa8411d35acbb91fe926",
"build/assets/pylib-android/smtpd.py": "https://files.ballistica.net/cache/ba1/06/02/b6a39c4e37133303bee16c3e28a4", "build/assets/pylib-android/smtpd.py": "https://files.ballistica.net/cache/ba1/06/02/b6a39c4e37133303bee16c3e28a4",
@ -2980,12 +2980,12 @@
"build/assets/pylib-android/string.py": "https://files.ballistica.net/cache/ba1/c0/72/d3a2d0337386d3235ba72f292752", "build/assets/pylib-android/string.py": "https://files.ballistica.net/cache/ba1/c0/72/d3a2d0337386d3235ba72f292752",
"build/assets/pylib-android/stringprep.py": "https://files.ballistica.net/cache/ba1/6a/56/35711a838f76fee7b52537bf8b45", "build/assets/pylib-android/stringprep.py": "https://files.ballistica.net/cache/ba1/6a/56/35711a838f76fee7b52537bf8b45",
"build/assets/pylib-android/struct.py": "https://files.ballistica.net/cache/ba1/4f/d2/88bd6537effd1d5edbba9d44dde2", "build/assets/pylib-android/struct.py": "https://files.ballistica.net/cache/ba1/4f/d2/88bd6537effd1d5edbba9d44dde2",
"build/assets/pylib-android/subprocess.py": "https://files.ballistica.net/cache/ba1/76/c5/0ab326efee3ecd6b54b3a9d0bd94", "build/assets/pylib-android/subprocess.py": "https://files.ballistica.net/cache/ba1/4d/65/22342cab21c558e60b2f50c5cedc",
"build/assets/pylib-android/sunau.py": "https://files.ballistica.net/cache/ba1/a0/34/779289fef0b2221345f40e042680", "build/assets/pylib-android/sunau.py": "https://files.ballistica.net/cache/ba1/a0/34/779289fef0b2221345f40e042680",
"build/assets/pylib-android/symtable.py": "https://files.ballistica.net/cache/ba1/0c/d4/a624b2e3304664f43189ed9e3bf7", "build/assets/pylib-android/symtable.py": "https://files.ballistica.net/cache/ba1/0c/d4/a624b2e3304664f43189ed9e3bf7",
"build/assets/pylib-android/sysconfig.py": "https://files.ballistica.net/cache/ba1/9f/fa/d6cfff49bc09fe36a33a5446c59d", "build/assets/pylib-android/sysconfig.py": "https://files.ballistica.net/cache/ba1/9f/fa/d6cfff49bc09fe36a33a5446c59d",
"build/assets/pylib-android/tabnanny.py": "https://files.ballistica.net/cache/ba1/84/61/4c63ead0ab71527cecdf3de68f1a", "build/assets/pylib-android/tabnanny.py": "https://files.ballistica.net/cache/ba1/84/61/4c63ead0ab71527cecdf3de68f1a",
"build/assets/pylib-android/tarfile.py": "https://files.ballistica.net/cache/ba1/76/bf/209364cd327ee75e9dac3211c882", "build/assets/pylib-android/tarfile.py": "https://files.ballistica.net/cache/ba1/a6/df/d478cf3917a29020944bff1a6f2d",
"build/assets/pylib-android/telnetlib.py": "https://files.ballistica.net/cache/ba1/1a/74/6e1fc988b9f5c12b9fd5a97c00ea", "build/assets/pylib-android/telnetlib.py": "https://files.ballistica.net/cache/ba1/1a/74/6e1fc988b9f5c12b9fd5a97c00ea",
"build/assets/pylib-android/tempfile.py": "https://files.ballistica.net/cache/ba1/43/60/07fbe6821c864a53861bd73b4d43", "build/assets/pylib-android/tempfile.py": "https://files.ballistica.net/cache/ba1/43/60/07fbe6821c864a53861bd73b4d43",
"build/assets/pylib-android/textwrap.py": "https://files.ballistica.net/cache/ba1/3e/b1/6a40553205dc96be5cb9039f3c8c", "build/assets/pylib-android/textwrap.py": "https://files.ballistica.net/cache/ba1/3e/b1/6a40553205dc96be5cb9039f3c8c",
@ -2998,22 +2998,22 @@
"build/assets/pylib-android/tomllib/_parser.py": "https://files.ballistica.net/cache/ba1/f9/a4/dc92c44403f970e94c2e494a7358", "build/assets/pylib-android/tomllib/_parser.py": "https://files.ballistica.net/cache/ba1/f9/a4/dc92c44403f970e94c2e494a7358",
"build/assets/pylib-android/tomllib/_re.py": "https://files.ballistica.net/cache/ba1/0e/50/9117e16c41c491615e06bb98861d", "build/assets/pylib-android/tomllib/_re.py": "https://files.ballistica.net/cache/ba1/0e/50/9117e16c41c491615e06bb98861d",
"build/assets/pylib-android/tomllib/_types.py": "https://files.ballistica.net/cache/ba1/07/be/9616d6f5e401fd31fbeea619fc97", "build/assets/pylib-android/tomllib/_types.py": "https://files.ballistica.net/cache/ba1/07/be/9616d6f5e401fd31fbeea619fc97",
"build/assets/pylib-android/trace.py": "https://files.ballistica.net/cache/ba1/c8/60/164b4f4317299db04e29070a163d", "build/assets/pylib-android/trace.py": "https://files.ballistica.net/cache/ba1/3d/86/98a2c3ec03dc0f394a2f48c2ffbc",
"build/assets/pylib-android/traceback.py": "https://files.ballistica.net/cache/ba1/bb/2d/c63a0bcd438697904573807bd392", "build/assets/pylib-android/traceback.py": "https://files.ballistica.net/cache/ba1/91/f6/7818e621e3b2f5bf583ed6863ef8",
"build/assets/pylib-android/tracemalloc.py": "https://files.ballistica.net/cache/ba1/e4/d1/0d2bee7773566e46797a939e5cbf", "build/assets/pylib-android/tracemalloc.py": "https://files.ballistica.net/cache/ba1/e4/d1/0d2bee7773566e46797a939e5cbf",
"build/assets/pylib-android/tty.py": "https://files.ballistica.net/cache/ba1/27/1c/7d61005a0a3c2c0952efc60dcb6d", "build/assets/pylib-android/tty.py": "https://files.ballistica.net/cache/ba1/27/1c/7d61005a0a3c2c0952efc60dcb6d",
"build/assets/pylib-android/types.py": "https://files.ballistica.net/cache/ba1/78/f8/942c08dbfc9c582f1bb8d5206639", "build/assets/pylib-android/types.py": "https://files.ballistica.net/cache/ba1/78/f8/942c08dbfc9c582f1bb8d5206639",
"build/assets/pylib-android/typing.py": "https://files.ballistica.net/cache/ba1/c0/7f/a037183494716cd57a7c9ac05d78", "build/assets/pylib-android/typing.py": "https://files.ballistica.net/cache/ba1/89/e0/654fda4fd2a09e93391179859cf0",
"build/assets/pylib-android/urllib/__init__.py": "https://files.ballistica.net/cache/ba1/34/0c/83beff7dcff8f5c7b87cd43cedaf", "build/assets/pylib-android/urllib/__init__.py": "https://files.ballistica.net/cache/ba1/34/0c/83beff7dcff8f5c7b87cd43cedaf",
"build/assets/pylib-android/urllib/error.py": "https://files.ballistica.net/cache/ba1/b7/dd/e0483ff647eb87162d6e19c04fa0", "build/assets/pylib-android/urllib/error.py": "https://files.ballistica.net/cache/ba1/b7/dd/e0483ff647eb87162d6e19c04fa0",
"build/assets/pylib-android/urllib/parse.py": "https://files.ballistica.net/cache/ba1/d4/72/83907682d432b511b85dd1e32572", "build/assets/pylib-android/urllib/parse.py": "https://files.ballistica.net/cache/ba1/dc/73/5bb3e0b069762ad988c1b55ce6e3",
"build/assets/pylib-android/urllib/request.py": "https://files.ballistica.net/cache/ba1/e8/9a/853d76344f1ca197926d49b3b9e6", "build/assets/pylib-android/urllib/request.py": "https://files.ballistica.net/cache/ba1/09/b8/666fe8319542f7247bb9f8573806",
"build/assets/pylib-android/urllib/response.py": "https://files.ballistica.net/cache/ba1/c8/53/7707a4b1e493c0ec4489ab523c93", "build/assets/pylib-android/urllib/response.py": "https://files.ballistica.net/cache/ba1/c8/53/7707a4b1e493c0ec4489ab523c93",
"build/assets/pylib-android/urllib/robotparser.py": "https://files.ballistica.net/cache/ba1/5a/76/16bdf398c166f953ad48c25506eb", "build/assets/pylib-android/urllib/robotparser.py": "https://files.ballistica.net/cache/ba1/5a/76/16bdf398c166f953ad48c25506eb",
"build/assets/pylib-android/uu.py": "https://files.ballistica.net/cache/ba1/93/26/8d78026ebde74a1ccd14024ae59f", "build/assets/pylib-android/uu.py": "https://files.ballistica.net/cache/ba1/7c/b7/95ca0c9e914550e6245dfe53c53e",
"build/assets/pylib-android/uuid.py": "https://files.ballistica.net/cache/ba1/75/5d/75cad456d0c3ab0c67f0276cca32", "build/assets/pylib-android/uuid.py": "https://files.ballistica.net/cache/ba1/75/5d/75cad456d0c3ab0c67f0276cca32",
"build/assets/pylib-android/warnings.py": "https://files.ballistica.net/cache/ba1/e5/f0/b4cbc35aae8d4b89a856b189b626", "build/assets/pylib-android/warnings.py": "https://files.ballistica.net/cache/ba1/e5/f0/b4cbc35aae8d4b89a856b189b626",
"build/assets/pylib-android/wave.py": "https://files.ballistica.net/cache/ba1/cb/12/5197ba1b7955bbee4ebde9f385ce", "build/assets/pylib-android/wave.py": "https://files.ballistica.net/cache/ba1/72/7a/8dacad95625ea14554b7d655ce2e",
"build/assets/pylib-android/weakref.py": "https://files.ballistica.net/cache/ba1/dd/14/612f02ca8acd723f633b6fff0adf", "build/assets/pylib-android/weakref.py": "https://files.ballistica.net/cache/ba1/dd/14/612f02ca8acd723f633b6fff0adf",
"build/assets/pylib-android/webbrowser.py": "https://files.ballistica.net/cache/ba1/81/85/b8d83c5311ddea5d88a1cfb89c3c", "build/assets/pylib-android/webbrowser.py": "https://files.ballistica.net/cache/ba1/81/85/b8d83c5311ddea5d88a1cfb89c3c",
"build/assets/pylib-android/xdrlib.py": "https://files.ballistica.net/cache/ba1/ee/12/e9067ab4904da935b8d2213426d8", "build/assets/pylib-android/xdrlib.py": "https://files.ballistica.net/cache/ba1/ee/12/e9067ab4904da935b8d2213426d8",
@ -3043,7 +3043,7 @@
"build/assets/pylib-android/xmlrpc/client.py": "https://files.ballistica.net/cache/ba1/8f/2c/ad71c1c87031be073184e71aa405", "build/assets/pylib-android/xmlrpc/client.py": "https://files.ballistica.net/cache/ba1/8f/2c/ad71c1c87031be073184e71aa405",
"build/assets/pylib-android/xmlrpc/server.py": "https://files.ballistica.net/cache/ba1/31/47/663bd28b952eef0e1316b94bacc6", "build/assets/pylib-android/xmlrpc/server.py": "https://files.ballistica.net/cache/ba1/31/47/663bd28b952eef0e1316b94bacc6",
"build/assets/pylib-android/zipapp.py": "https://files.ballistica.net/cache/ba1/d9/2e/a793f092af6383a6383678cf7b73", "build/assets/pylib-android/zipapp.py": "https://files.ballistica.net/cache/ba1/d9/2e/a793f092af6383a6383678cf7b73",
"build/assets/pylib-android/zipfile.py": "https://files.ballistica.net/cache/ba1/21/cd/b798a8f660aa3ec322ce59bddac9", "build/assets/pylib-android/zipfile.py": "https://files.ballistica.net/cache/ba1/2c/d1/a57574daf0ec6456dc2d0858952d",
"build/assets/pylib-android/zipimport.py": "https://files.ballistica.net/cache/ba1/6d/3b/32a7258f670eb71daf3a8492a4f1", "build/assets/pylib-android/zipimport.py": "https://files.ballistica.net/cache/ba1/6d/3b/32a7258f670eb71daf3a8492a4f1",
"build/assets/pylib-android/zoneinfo/__init__.py": "https://files.ballistica.net/cache/ba1/0d/f5/e8a66015e2fe2d77f2e4a5dea1ff", "build/assets/pylib-android/zoneinfo/__init__.py": "https://files.ballistica.net/cache/ba1/0d/f5/e8a66015e2fe2d77f2e4a5dea1ff",
"build/assets/pylib-android/zoneinfo/_common.py": "https://files.ballistica.net/cache/ba1/5b/52/bdac4156dcbac96743fa99468bf2", "build/assets/pylib-android/zoneinfo/_common.py": "https://files.ballistica.net/cache/ba1/5b/52/bdac4156dcbac96743fa99468bf2",
@ -3064,7 +3064,7 @@
"build/assets/pylib-apple/_pyio.py": "https://files.ballistica.net/cache/ba1/a6/e8/8d66fbca88b13213cdd2177390b8", "build/assets/pylib-apple/_pyio.py": "https://files.ballistica.net/cache/ba1/a6/e8/8d66fbca88b13213cdd2177390b8",
"build/assets/pylib-apple/_sitebuiltins.py": "https://files.ballistica.net/cache/ba1/8b/5e/3f6e73917962fa014ad2c4a55e61", "build/assets/pylib-apple/_sitebuiltins.py": "https://files.ballistica.net/cache/ba1/8b/5e/3f6e73917962fa014ad2c4a55e61",
"build/assets/pylib-apple/_strptime.py": "https://files.ballistica.net/cache/ba1/ff/69/9c3f7647db7621bb88c43cc282d3", "build/assets/pylib-apple/_strptime.py": "https://files.ballistica.net/cache/ba1/ff/69/9c3f7647db7621bb88c43cc282d3",
"build/assets/pylib-apple/_sysconfigdata__darwin_darwin.py": "https://files.ballistica.net/cache/ba1/c8/9e/dcdc08ac22a30cac42dad0f099e6", "build/assets/pylib-apple/_sysconfigdata__darwin_darwin.py": "https://files.ballistica.net/cache/ba1/1d/eb/8b4ea385ac52e2fc30eb5b7486d9",
"build/assets/pylib-apple/_sysconfigdata__ios_iphoneos.py": "https://files.ballistica.net/cache/ba1/5b/fc/ed73a12261ecc310bffce6786cd2", "build/assets/pylib-apple/_sysconfigdata__ios_iphoneos.py": "https://files.ballistica.net/cache/ba1/5b/fc/ed73a12261ecc310bffce6786cd2",
"build/assets/pylib-apple/_sysconfigdata__ios_iphoneos_arm64.py": "https://files.ballistica.net/cache/ba1/13/cd/af0612cecdfdea79507be605b5fa", "build/assets/pylib-apple/_sysconfigdata__ios_iphoneos_arm64.py": "https://files.ballistica.net/cache/ba1/13/cd/af0612cecdfdea79507be605b5fa",
"build/assets/pylib-apple/_sysconfigdata__ios_iphonesimulator.py": "https://files.ballistica.net/cache/ba1/f9/c9/55d9c5c9cf5a0a026d084a1f9519", "build/assets/pylib-apple/_sysconfigdata__ios_iphonesimulator.py": "https://files.ballistica.net/cache/ba1/f9/c9/55d9c5c9cf5a0a026d084a1f9519",
@ -3075,7 +3075,7 @@
"build/assets/pylib-apple/_sysconfigdata__tvos_appletvsimulator.py": "https://files.ballistica.net/cache/ba1/a3/c8/8367fb568e377ef2a72f0e9c2394", "build/assets/pylib-apple/_sysconfigdata__tvos_appletvsimulator.py": "https://files.ballistica.net/cache/ba1/a3/c8/8367fb568e377ef2a72f0e9c2394",
"build/assets/pylib-apple/_sysconfigdata__tvos_appletvsimulator_arm64.py": "https://files.ballistica.net/cache/ba1/bf/5e/3b80c0bd0f6e8d00372d4f6f1ae5", "build/assets/pylib-apple/_sysconfigdata__tvos_appletvsimulator_arm64.py": "https://files.ballistica.net/cache/ba1/bf/5e/3b80c0bd0f6e8d00372d4f6f1ae5",
"build/assets/pylib-apple/_sysconfigdata__tvos_appletvsimulator_x86_64.py": "https://files.ballistica.net/cache/ba1/48/28/cee78f35df83a94a308919080918", "build/assets/pylib-apple/_sysconfigdata__tvos_appletvsimulator_x86_64.py": "https://files.ballistica.net/cache/ba1/48/28/cee78f35df83a94a308919080918",
"build/assets/pylib-apple/_sysconfigdata_d_darwin_darwin.py": "https://files.ballistica.net/cache/ba1/1c/01/95f871b00015f14a9fabd2e1f645", "build/assets/pylib-apple/_sysconfigdata_d_darwin_darwin.py": "https://files.ballistica.net/cache/ba1/9e/b9/1d9cd5328d59ceb9047d210beaba",
"build/assets/pylib-apple/_sysconfigdata_d_ios_iphoneos.py": "https://files.ballistica.net/cache/ba1/5b/fc/ed73a12261ecc310bffce6786cd2", "build/assets/pylib-apple/_sysconfigdata_d_ios_iphoneos.py": "https://files.ballistica.net/cache/ba1/5b/fc/ed73a12261ecc310bffce6786cd2",
"build/assets/pylib-apple/_sysconfigdata_d_ios_iphoneos_arm64.py": "https://files.ballistica.net/cache/ba1/11/62/2b9e1d51ef4a4225099acb1e8200", "build/assets/pylib-apple/_sysconfigdata_d_ios_iphoneos_arm64.py": "https://files.ballistica.net/cache/ba1/11/62/2b9e1d51ef4a4225099acb1e8200",
"build/assets/pylib-apple/_sysconfigdata_d_ios_iphonesimulator.py": "https://files.ballistica.net/cache/ba1/f9/c9/55d9c5c9cf5a0a026d084a1f9519", "build/assets/pylib-apple/_sysconfigdata_d_ios_iphonesimulator.py": "https://files.ballistica.net/cache/ba1/f9/c9/55d9c5c9cf5a0a026d084a1f9519",
@ -4068,26 +4068,26 @@
"build/assets/windows/Win32/ucrtbased.dll": "https://files.ballistica.net/cache/ba1/2d/ef/5335207d41b21b9823f6805997f1", "build/assets/windows/Win32/ucrtbased.dll": "https://files.ballistica.net/cache/ba1/2d/ef/5335207d41b21b9823f6805997f1",
"build/assets/windows/Win32/vc_redist.x86.exe": "https://files.ballistica.net/cache/ba1/b0/8a/55e2e77623fe657bea24f223a3ae", "build/assets/windows/Win32/vc_redist.x86.exe": "https://files.ballistica.net/cache/ba1/b0/8a/55e2e77623fe657bea24f223a3ae",
"build/assets/windows/Win32/vcruntime140d.dll": "https://files.ballistica.net/cache/ba1/86/5b/2af4d1e26a1a8073c89acb06e599", "build/assets/windows/Win32/vcruntime140d.dll": "https://files.ballistica.net/cache/ba1/86/5b/2af4d1e26a1a8073c89acb06e599",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/05/39/9f392f63f9383e8ef3457d08f362", "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/7a/5c/7f2aaa9051adf632dc957f61ef55",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/ea/05/7ea22ed9c5f1f514b9e39d90cde6", "build/prefab/full/linux_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/ae/11/e739a151be864da4c87c15c155fa",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/46/1c/bd65d52f0848387e56a536a346a5", "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/bc/0c/f1a578e5329a81a91618b03f7029",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/40/b8/fa9aeee2292fde22215797faa75b", "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/32/e1/c608186cfa17d5ebc93e676166bd",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/78/b0/3d856abf820b6410d2729246b3c2", "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/0f/b0/bfd562781cf6ae1b0224d76e7c3b",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/a1/85/9ca2209cd2baf659b4e2b40b79fd", "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/f3/a9/1b6cce29b356c2317b6f1126ba9b",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/3b/e4/d49571e34cf479114d9cf4742737", "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/67/ac/03c3e7d5499a978e2af2099aae55",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/e7/b8/53a5666e993663054202393e589b", "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/22/c0/4b32851978bf97cccd4c45f71a43",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/8f/6a/9b8d0b4b6dd97f4d5d4b48f5b84d", "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/90/28/e0d6e0de8df08e0537816c8838a0",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/48/1d/c9762e55356a288aebfefecbd43e", "build/prefab/full/mac_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/3d/c5/ffa0a2051e467d002bc272316da6",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/14/9f/91b6ce7c3282ae203703618627a9", "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/9f/fe/5e9e6873b520b720754ab1794b3c",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/78/71/d5ab66d0f577a746e05f44d753bd", "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/62/8f/4ffeb86197612d84f648141e5dd4",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/33/67/e32865d5160bb26183b8b4257a88", "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/b6/ca/46a742136cc8824e171de4c8e683",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/85/ba/98322913ba70e71e3858ee7916e5", "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/c0/d6/18ad1186b589cd30d0f252b0b542",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/12/37/ca8d54ec589bc95dd9030a635035", "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/12/df/24c41de1b3bab9d010b17969c69f",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/9f/b7/fdec2c3c131abe175e716698605e", "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/75/c2/8f3635450156521401ff37c6070c",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/08/59/d701c12712effe14b89ea37c013f", "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/8e/df/6d1d1ef0ed45c6a81f60e7dbc3a5",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/99/01/8c49ac6d34dc7219ccf047794437", "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/bf/1e/d1bf7b259d98fd464889aa755c22",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/51/ae/49b7723adac6c2e763d03bf27b16", "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/46/1b/20394315126f1f9aa200bd736ad7",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/32/18/35d3b785547afc61cfb6b3ba3504", "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/25/36/a159b2c628f6cbbbcc7aa8f90b12",
"build/prefab/lib/linux_arm64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/be/19/b5458933dfc7371d91ecfcd2e06f", "build/prefab/lib/linux_arm64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/be/19/b5458933dfc7371d91ecfcd2e06f",
"build/prefab/lib/linux_arm64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/4e/48/123b806cbe6ddb3d9a8368bbb4f8", "build/prefab/lib/linux_arm64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/4e/48/123b806cbe6ddb3d9a8368bbb4f8",
"build/prefab/lib/linux_arm64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/be/19/b5458933dfc7371d91ecfcd2e06f", "build/prefab/lib/linux_arm64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/be/19/b5458933dfc7371d91ecfcd2e06f",
@ -4104,18 +4104,18 @@
"build/prefab/lib/mac_x86_64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/7e/fa/291fd7e935502ced7e99b8c8f7f0", "build/prefab/lib/mac_x86_64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/7e/fa/291fd7e935502ced7e99b8c8f7f0",
"build/prefab/lib/mac_x86_64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/e1/cb/7e8440699e59e8646da25aa5782b", "build/prefab/lib/mac_x86_64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/e1/cb/7e8440699e59e8646da25aa5782b",
"build/prefab/lib/mac_x86_64_server/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/7e/fa/291fd7e935502ced7e99b8c8f7f0", "build/prefab/lib/mac_x86_64_server/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/7e/fa/291fd7e935502ced7e99b8c8f7f0",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/5c/4e/26f72e68d55dcf12d62b1274b7ad", "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/66/17/448c77d6413f25555e40055d154b",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/c3/61/d40dfab20f59da7155b942632066", "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/56/b0/7ce3384cfb63d3a224e317c69fa8",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/a9/f7/ddae4d7e7e59f0b9db4eee09d226", "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/b6/73/847160ecfbbbea758f130c03abb8",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/4a/a4/c9c09853640eb5a5f5a0ce4cf71c", "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/dc/e6/e052a9115ca83686c6634d2c26ff",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/9d/42/a7bc0e69180e2e45e5b5b93cc49c", "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/2e/56/390279b063b7f8455cf7f1656608",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/9a/a7/5e31d7b317e4544feff2eabad4a5", "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/3a/d7/a0329b38db9b594c36ff88a884c9",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/3b/74/84e7f0fb207da9a99a0c71b12a2e", "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/a5/9b/0802a9d6604fffc0136ac95a5be3",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/2e/dc/4406fedd025a80037e395097ee19", "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/e9/74/5d6a32e3a161a2f5597aa9336b79",
"src/assets/ba_data/python/babase/_mgen/__init__.py": "https://files.ballistica.net/cache/ba1/f8/85/fed7f2ed98ff2ba271f9dbe3391c", "src/assets/ba_data/python/babase/_mgen/__init__.py": "https://files.ballistica.net/cache/ba1/f8/85/fed7f2ed98ff2ba271f9dbe3391c",
"src/assets/ba_data/python/babase/_mgen/enums.py": "https://files.ballistica.net/cache/ba1/48/4b/e6974f0a4d14be8213dc00d971c3", "src/assets/ba_data/python/babase/_mgen/enums.py": "https://files.ballistica.net/cache/ba1/48/4b/e6974f0a4d14be8213dc00d971c3",
"src/ballistica/base/mgen/pyembed/binding_base.inc": "https://files.ballistica.net/cache/ba1/3e/7a/203e2a5d2b5bb42cfe3fd2fe16c2", "src/ballistica/base/mgen/pyembed/binding_base.inc": "https://files.ballistica.net/cache/ba1/3e/7a/203e2a5d2b5bb42cfe3fd2fe16c2",
"src/ballistica/base/mgen/pyembed/binding_base_app.inc": "https://files.ballistica.net/cache/ba1/41/38/52cb9a73efef37404cec22fca18e", "src/ballistica/base/mgen/pyembed/binding_base_app.inc": "https://files.ballistica.net/cache/ba1/c2/f2/cb8d052d76a1f4abe163cab2276e",
"src/ballistica/classic/mgen/pyembed/binding_classic.inc": "https://files.ballistica.net/cache/ba1/3c/eb/412513963f0818ab39c58bf292e3", "src/ballistica/classic/mgen/pyembed/binding_classic.inc": "https://files.ballistica.net/cache/ba1/3c/eb/412513963f0818ab39c58bf292e3",
"src/ballistica/core/mgen/pyembed/binding_core.inc": "https://files.ballistica.net/cache/ba1/9d/0a/3c9636138e35284923e0c8311c69", "src/ballistica/core/mgen/pyembed/binding_core.inc": "https://files.ballistica.net/cache/ba1/9d/0a/3c9636138e35284923e0c8311c69",
"src/ballistica/core/mgen/pyembed/env.inc": "https://files.ballistica.net/cache/ba1/8b/e4/6e5818f360d10b7b0224a9e91d07", "src/ballistica/core/mgen/pyembed/env.inc": "https://files.ballistica.net/cache/ba1/8b/e4/6e5818f360d10b7b0224a9e91d07",

View File

@ -1,6 +1,9 @@
### 1.7.21 (build 21143, api 8, 2023-06-25) ### 1.7.21 (build 21147, api 8, 2023-06-26)
- Fixed an issue where server builds would not always include collision meshes. - Fixed an issue where server builds would not always include collision meshes.
- Upgraded Python to 3.11.4 on Android builds.
- Cleaned up the language subsystem and the process for applying app-config
changes a bit. Please holler if you see weirdness in either.
### 1.7.20 (build 21140, api 8, 2023-06-22) ### 1.7.20 (build 21140, api 8, 2023-06-22)
@ -360,8 +363,8 @@
use log calls instead of prints. The environment vars to enable them are now use log calls instead of prints. The environment vars to enable them are now
`BA_DEBUG_LOG_CONNECTIVITY` and `BA_DEBUG_LOG_V2_TRANSPORT`. Set either to '1' `BA_DEBUG_LOG_CONNECTIVITY` and `BA_DEBUG_LOG_V2_TRANSPORT`. Set either to '1'
to enable debug logging. to enable debug logging.
- (build 21125) Fixed a bug where feature-sets would not have their - (build 21125) Fixed a bug where app-modes would not have their
DoApplyConfig callbacks called in C++, which was causing the server-mode DoApplyAppConfig callbacks called in C++, which was causing the server-mode
`idle_exit_minutes` value to be ignored. Servers should now properly exit `idle_exit_minutes` value to be ignored. Servers should now properly exit
after being idle for this length of time. after being idle for this length of time.
- (build 21126) Reworked the efrocache system used by public builds for - (build 21126) Reworked the efrocache system used by public builds for

View File

@ -21,14 +21,14 @@ with specific naming conventions:
in this directory called `featureset_foo_bar.py`. in this directory called `featureset_foo_bar.py`.
- **Python Package**: If feature set `foo_bar` provides a Python package, it - **Python Package**: If feature set `foo_bar` provides a Python package, it
should be a directory named `bafoobar` ('ba' prefix, name with spaces removed) should be a directory named `bafoobar` ('ba' prefix, name with spaces removed)
that lives under [Python source files](../../src/assets/ba_data/python). that lives under [src/assets/ba_data/python](../../src/assets/ba_data/python).
- **Native Code**: If feature set `foo_bar` provides a native component (C++ - **Native Code**: If feature set `foo_bar` provides a native component (C++
code or otherwise) it should live in a directory named `foo_bar` (unmodified code or otherwise) it should live in a directory named `foo_bar` (unmodified
feature set name) under [native source files](../../src/ballistica). feature set name) under [src/ballistica](../../src/ballistica).
- **Meta Package**: If feature set `foo_bar` provides a meta package (that is, - **Meta Package**: If feature set `foo_bar` provides a meta package (that is,
code or data used to generate other source code), it should be a directory code or data used to generate other source code), it should be a directory
named `bafoobarmeta` ('ba' prefix, name with spaces removed, 'meta' suffix) named `bafoobarmeta` ('ba' prefix, name with spaces removed, 'meta' suffix)
that lives under [meta source files](../../src/meta). that lives under [src/meta](../../src/meta).
- **Test Package**: If feature set `foo_bar` provides a set of tests, it should - **Test Package**: If feature set `foo_bar` provides a set of tests, it should
be a directory named `test_foo_bar` ('test_' prefix, unmodified feature set be a directory named `test_foo_bar` ('test_' prefix, unmodified feature set
name) under [tests](../../tests). name) under [tests](../../tests).

View File

@ -54,8 +54,8 @@ class App:
# pylint: disable=too-many-public-methods # pylint: disable=too-many-public-methods
plugins: PluginSubsystem plugins: PluginSubsystem
lang: LanguageSubsystem
# log_handler: LogHandler
health_monitor: AppHealthMonitor health_monitor: AppHealthMonitor
class State(Enum): class State(Enum):
@ -247,6 +247,7 @@ class App:
self._called_on_app_running = False self._called_on_app_running = False
self._app_paused = False self._app_paused = False
self._subsystem_registration_ended = False self._subsystem_registration_ended = False
self._pending_apply_app_config = False
# Config. # Config.
self.config_file_healthy = False self.config_file_healthy = False
@ -281,7 +282,6 @@ class App:
self.components = AppComponentSubsystem() self.components = AppComponentSubsystem()
self.meta = MetadataSubsystem() self.meta = MetadataSubsystem()
self.lang = LanguageSubsystem()
self.net = NetworkSubsystem() self.net = NetworkSubsystem()
self.workspaces = WorkspaceSubsystem() self.workspaces = WorkspaceSubsystem()
self._pending_intent: AppIntent | None = None self._pending_intent: AppIntent | None = None
@ -297,7 +297,7 @@ class App:
self._asyncio_timer: babase.AppTimer | None = None self._asyncio_timer: babase.AppTimer | None = None
def postinit(self) -> None: def postinit(self) -> None:
"""Called after we are inited and assigned to babase.app.""" """Called after we've been inited and assigned to babase.app."""
if os.environ.get('BA_RUNNING_WITH_DUMMY_MODULES') == '1': if os.environ.get('BA_RUNNING_WITH_DUMMY_MODULES') == '1':
return return
@ -306,6 +306,7 @@ class App:
# some of this stuff accesses babase.app and that doesn't # some of this stuff accesses babase.app and that doesn't
# exist yet as of our __init__() call. # exist yet as of our __init__() call.
self.lang = LanguageSubsystem()
self.plugins = PluginSubsystem() self.plugins = PluginSubsystem()
def register_subsystem(self, subsystem: AppSubsystem) -> None: def register_subsystem(self, subsystem: AppSubsystem) -> None:
@ -622,6 +623,39 @@ class App:
# plugin hasn't already told it to do something. # plugin hasn't already told it to do something.
self.set_intent(AppIntentDefault()) self.set_intent(AppIntentDefault())
def push_apply_app_config(self) -> None:
"""Internal. Use app.config.apply() to apply app config changes."""
# To be safe, let's run this by itself in the event loop.
# This avoids potential trouble if this gets called mid-draw or
# something like that.
self._pending_apply_app_config = True
_babase.pushcall(self._apply_app_config, raw=True)
def _apply_app_config(self) -> None:
assert _babase.in_logic_thread()
_babase.lifecyclelog('apply-app-config')
# If multiple apply calls have been made, only actually apply once.
if not self._pending_apply_app_config:
return
_pending_apply_app_config = False
# Inform all app subsystems in the same order they were inited.
# Operate on a copy here because subsystems may still be able to
# be added at this point.
for subsystem in self._subsystems.copy():
try:
subsystem.do_apply_app_config()
except Exception:
logging.exception(
'Error in do_apply_app_config for subsystem %s.', subsystem
)
# Let the native layer do its thing.
_babase.do_apply_app_config()
class DefaultAppModeSelector(AppModeSelector): class DefaultAppModeSelector(AppModeSelector):
"""Decides which app modes to use to handle intents. """Decides which app modes to use to handle intents.

View File

@ -10,6 +10,8 @@ import _babase
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Any from typing import Any
_g_pending_apply = False # pylint: disable=invalid-name
class AppConfig(dict): class AppConfig(dict):
"""A special dict that holds the game's persistent configuration values. """A special dict that holds the game's persistent configuration values.
@ -74,8 +76,12 @@ class AppConfig(dict):
return _babase.get_appconfig_builtin_keys() return _babase.get_appconfig_builtin_keys()
def apply(self) -> None: def apply(self) -> None:
"""Apply config values to the running app.""" """Apply config values to the running app.
_babase.apply_config()
This call is thread-safe and asynchronous; changes will happen
in the next logic event loop cycle.
"""
_babase.app.push_apply_app_config()
def commit(self) -> None: def commit(self) -> None:
"""Commits the config to local storage. """Commits the config to local storage.

View File

@ -47,3 +47,6 @@ class AppSubsystem:
def on_app_shutdown(self) -> None: def on_app_shutdown(self) -> None:
"""Called when the app is shutting down.""" """Called when the app is shutting down."""
def do_apply_app_config(self) -> None:
"""Called when the app config should be applied."""

View File

@ -9,6 +9,7 @@ import logging
from typing import TYPE_CHECKING, overload from typing import TYPE_CHECKING, overload
import _babase import _babase
from babase._appsubsystem import AppSubsystem
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Any, Sequence from typing import Any, Sequence
@ -16,44 +17,21 @@ if TYPE_CHECKING:
import babase import babase
class LanguageSubsystem: class LanguageSubsystem(AppSubsystem):
"""Wraps up language related app functionality. """Language functionality for the app.
Category: **App Classes** Category: **App Classes**
To use this class, access the single instance of it at 'ba.app.lang'. Access the single instance of this class at 'babase.app.lang'.
""" """
def __init__(self) -> None: def __init__(self) -> None:
self.language_target: AttrDict | None = None super().__init__()
self.language_merged: AttrDict | None = None self.default_language: str = self._get_default_language()
self.default_language = self._get_default_language()
def _can_display_language(self, language: str) -> bool: self._language: str | None = None
"""Tell whether we can display a particular language. self._language_target: AttrDict | None = None
self._language_merged: AttrDict | None = None
On some platforms we don't have unicode rendering yet
which limits the languages we can draw.
"""
# We don't yet support full unicode display on windows or linux :-(.
if (
language
in {
'Chinese',
'ChineseTraditional',
'Persian',
'Korean',
'Arabic',
'Hindi',
'Vietnamese',
'Thai',
'Tamil',
}
and not _babase.can_display_full_unicode()
):
return False
return True
@property @property
def locale(self) -> str: def locale(self) -> str:
@ -67,62 +45,16 @@ class LanguageSubsystem:
assert isinstance(env['locale'], str) assert isinstance(env['locale'], str)
return env['locale'] return env['locale']
def _get_default_language(self) -> str:
languages = {
'ar': 'Arabic',
'be': 'Belarussian',
'zh': 'Chinese',
'hr': 'Croatian',
'cs': 'Czech',
'da': 'Danish',
'nl': 'Dutch',
'eo': 'Esperanto',
'fil': 'Filipino',
'fr': 'French',
'de': 'German',
'el': 'Greek',
'hi': 'Hindi',
'hu': 'Hungarian',
'id': 'Indonesian',
'it': 'Italian',
'ko': 'Korean',
'ms': 'Malay',
'fa': 'Persian',
'pl': 'Polish',
'pt': 'Portuguese',
'ro': 'Romanian',
'ru': 'Russian',
'sr': 'Serbian',
'es': 'Spanish',
'sk': 'Slovak',
'sv': 'Swedish',
'ta': 'Tamil',
'th': 'Thai',
'tr': 'Turkish',
'uk': 'Ukrainian',
'vec': 'Venetian',
'vi': 'Vietnamese',
}
# Special case for Chinese: map specific variations to traditional.
# (otherwise will map to 'Chinese' which is simplified)
if self.locale in ('zh_HANT', 'zh_TW'):
language = 'ChineseTraditional'
else:
language = languages.get(self.locale[:2], 'English')
if not self._can_display_language(language):
language = 'English'
return language
@property @property
def language(self) -> str: def language(self) -> str:
"""The name of the language the game is running in. """The current active language for the app.
This can be selected explicitly by the user or may be set This can be selected explicitly by the user or may be set
automatically based on babase.App.locale or other factors. automatically based on locale or other factors.
""" """
assert isinstance(_babase.app.config, dict) if self._language is None:
return _babase.app.config.get('Lang', self.default_language) raise RuntimeError('App language is not yet set.')
return self._language
@property @property
def available_languages(self) -> list[str]: def available_languages(self) -> list[str]:
@ -164,13 +96,14 @@ class LanguageSubsystem:
print_change: bool = True, print_change: bool = True,
store_to_config: bool = True, store_to_config: bool = True,
) -> None: ) -> None:
"""Set the active language used for the game. """Set the active app language.
Pass None to use OS default language. Pass None to use OS default language.
""" """
# pylint: disable=too-many-locals # pylint: disable=too-many-locals
# pylint: disable=too-many-statements # pylint: disable=too-many-statements
# pylint: disable=too-many-branches # pylint: disable=too-many-branches
assert _babase.in_logic_thread()
cfg = _babase.app.config cfg = _babase.app.config
cur_language = cfg.get('Lang', None) cur_language = cfg.get('Lang', None)
@ -215,38 +148,36 @@ class LanguageSubsystem:
with open(lmodfile, encoding='utf-8') as infile: with open(lmodfile, encoding='utf-8') as infile:
lmodvalues = json.loads(infile.read()) lmodvalues = json.loads(infile.read())
except Exception: except Exception:
from babase import _error logging.exception("Error importing language '%s'.", language)
_error.print_exception('Exception importing language:', language)
_babase.screenmessage( _babase.screenmessage(
"Error setting language to '" f"Error setting language to '{language}'; see log for details.",
+ language
+ "'; see log for details",
color=(1, 0, 0), color=(1, 0, 0),
) )
switched = False switched = False
lmodvalues = None lmodvalues = None
self._language = language
# Create an attrdict of *just* our target language. # Create an attrdict of *just* our target language.
self.language_target = AttrDict() self._language_target = AttrDict()
langtarget = self.language_target langtarget = self._language_target
assert langtarget is not None assert langtarget is not None
_add_to_attr_dict( _add_to_attr_dict(
langtarget, lmodvalues if lmodvalues is not None else lenglishvalues langtarget, lmodvalues if lmodvalues is not None else lenglishvalues
) )
# Create an attrdict of our target language overlaid # Create an attrdict of our target language overlaid on our base
# on our base (english). # (english).
languages = [lenglishvalues] languages = [lenglishvalues]
if lmodvalues is not None: if lmodvalues is not None:
languages.append(lmodvalues) languages.append(lmodvalues)
lfull = AttrDict() lfull = AttrDict()
for lmod in languages: for lmod in languages:
_add_to_attr_dict(lfull, lmod) _add_to_attr_dict(lfull, lmod)
self.language_merged = lfull self._language_merged = lfull
# Pass some keys/values in for low level code to use; # Pass some keys/values in for low level code to use; start with
# start with everything in their 'internal' section. # everything in their 'internal' section.
internal_vals = [ internal_vals = [
v for v in list(lfull['internal'].items()) if isinstance(v[1], str) v for v in list(lfull['internal'].items()) if isinstance(v[1], str)
] ]
@ -265,7 +196,7 @@ class LanguageSubsystem:
('axisText', lfull['configGamepadWindow']['axisText']) ('axisText', lfull['configGamepadWindow']['axisText'])
) )
internal_vals.append(('buttonText', lfull['buttonText'])) internal_vals.append(('buttonText', lfull['buttonText']))
lmerged = self.language_merged lmerged = self._language_merged
assert lmerged is not None assert lmerged is not None
random_names = [ random_names = [
n.strip() for n in lmerged['randomPlayerNamesText'].split(',') n.strip() for n in lmerged['randomPlayerNamesText'].split(',')
@ -283,6 +214,13 @@ class LanguageSubsystem:
color=(0, 1, 0), color=(0, 1, 0),
) )
def do_apply_app_config(self) -> None:
assert _babase.in_logic_thread()
assert isinstance(_babase.app.config, dict)
lang = _babase.app.config.get('Lang', self.default_language)
if lang != self._language:
self.setlanguage(lang, print_change=False, store_to_config=False)
def get_resource( def get_resource(
self, self,
resource: str, resource: str,
@ -294,38 +232,30 @@ class LanguageSubsystem:
DEPRECATED; use babase.Lstr functionality for these purposes. DEPRECATED; use babase.Lstr functionality for these purposes.
""" """
try: try:
# If we have no language set, go ahead and set it. # If we have no language set, try and set it to english.
if self.language_merged is None: # Also make a fuss because we should try to avoid this.
language = self.language if self._language_merged is None:
try: try:
if _babase.do_once():
logging.warning(
'get_resource() called before language'
' set; falling back to english.'
)
self.setlanguage( self.setlanguage(
language, print_change=False, store_to_config=False 'English', print_change=False, store_to_config=False
) )
except Exception: except Exception:
from babase import _error logging.exception(
'Error setting fallback english language.'
logging.exception('Error setting language to %s.', language) )
raise
# Try english as a fallback.
if language != 'English':
print('Resorting to fallback language (English)')
try:
self.setlanguage(
'English',
print_change=False,
store_to_config=False,
)
except Exception:
_error.print_exception(
'error setting language to english fallback'
)
# If they provided a fallback_resource value, try the # If they provided a fallback_resource value, try the
# target-language-only dict first and then fall back to trying the # target-language-only dict first and then fall back to
# fallback_resource value in the merged dict. # trying the fallback_resource value in the merged dict.
if fallback_resource is not None: if fallback_resource is not None:
try: try:
values = self.language_target values = self._language_target
splits = resource.split('.') splits = resource.split('.')
dicts = splits[:-1] dicts = splits[:-1]
key = splits[-1] key = splits[-1]
@ -336,11 +266,11 @@ class LanguageSubsystem:
val = values[key] val = values[key]
return val return val
except Exception: except Exception:
# FIXME: Shouldn't we try the fallback resource in the # FIXME: Shouldn't we try the fallback resource in
# merged dict AFTER we try the main resource in the # the merged dict AFTER we try the main resource in
# merged dict? # the merged dict?
try: try:
values = self.language_merged values = self._language_merged
splits = fallback_resource.split('.') splits = fallback_resource.split('.')
dicts = splits[:-1] dicts = splits[:-1]
key = splits[-1] key = splits[-1]
@ -352,14 +282,15 @@ class LanguageSubsystem:
return val return val
except Exception: except Exception:
# If we got nothing for fallback_resource, default # If we got nothing for fallback_resource,
# to the normal code which checks or primary # default to the normal code which checks or
# value in the merge dict; there's a chance we can # primary value in the merge dict; there's a
# get an english value for it (which we weren't # chance we can get an english value for it
# looking for the first time through). # (which we weren't looking for the first time
# through).
pass pass
values = self.language_merged values = self._language_merged
splits = resource.split('.') splits = resource.split('.')
dicts = splits[:-1] dicts = splits[:-1]
key = splits[-1] key = splits[-1]
@ -371,9 +302,9 @@ class LanguageSubsystem:
return val return val
except Exception: except Exception:
# Ok, looks like we couldn't find our main or fallback resource # Ok, looks like we couldn't find our main or fallback
# anywhere. Now if we've been given a fallback value, return it; # resource anywhere. Now if we've been given a fallback
# otherwise fail. # value, return it; otherwise fail.
from babase import _error from babase import _error
if fallback_value is not None: if fallback_value is not None:
@ -426,18 +357,93 @@ class LanguageSubsystem:
raise ValueError('Invalid Input; must be length 1') raise ValueError('Invalid Input; must be length 1')
return 0xE000 <= ord(char) <= 0xF8FF return 0xE000 <= ord(char) <= 0xF8FF
def _can_display_language(self, language: str) -> bool:
"""Tell whether we can display a particular language.
On some platforms we don't have unicode rendering yet which
limits the languages we can draw.
"""
# We don't yet support full unicode display on windows or linux :-(.
if (
language
in {
'Chinese',
'ChineseTraditional',
'Persian',
'Korean',
'Arabic',
'Hindi',
'Vietnamese',
'Thai',
'Tamil',
}
and not _babase.can_display_full_unicode()
):
return False
return True
def _get_default_language(self) -> str:
languages = {
'ar': 'Arabic',
'be': 'Belarussian',
'zh': 'Chinese',
'hr': 'Croatian',
'cs': 'Czech',
'da': 'Danish',
'nl': 'Dutch',
'eo': 'Esperanto',
'fil': 'Filipino',
'fr': 'French',
'de': 'German',
'el': 'Greek',
'hi': 'Hindi',
'hu': 'Hungarian',
'id': 'Indonesian',
'it': 'Italian',
'ko': 'Korean',
'ms': 'Malay',
'fa': 'Persian',
'pl': 'Polish',
'pt': 'Portuguese',
'ro': 'Romanian',
'ru': 'Russian',
'sr': 'Serbian',
'es': 'Spanish',
'sk': 'Slovak',
'sv': 'Swedish',
'ta': 'Tamil',
'th': 'Thai',
'tr': 'Turkish',
'uk': 'Ukrainian',
'vec': 'Venetian',
'vi': 'Vietnamese',
}
# Special case for Chinese: map specific variations to
# traditional. (otherwise will map to 'Chinese' which is
# simplified)
if self.locale in ('zh_HANT', 'zh_TW'):
language = 'ChineseTraditional'
else:
language = languages.get(self.locale[:2], 'English')
if not self._can_display_language(language):
language = 'English'
return language
class Lstr: class Lstr:
"""Used to define strings in a language-independent way. """Used to define strings in a language-independent way.
Category: **General Utility Classes** Category: **General Utility Classes**
These should be used whenever possible in place of hard-coded strings These should be used whenever possible in place of hard-coded
so that in-game or UI elements show up correctly on all clients in their strings so that in-game or UI elements show up correctly on all
currently-active language. clients in their currently-active language.
To see available resource keys, look at any of the bs_language_*.py files To see available resource keys, look at any of the bs_language_*.py
in the game or the translations pages at legacy.ballistica.net/translate. files in the game or the translations pages at
legacy.ballistica.net/translate.
##### Examples ##### Examples
EXAMPLE 1: specify a string from a resource path EXAMPLE 1: specify a string from a resource path
@ -573,10 +579,10 @@ class Lstr:
def is_flat_value(self) -> bool: def is_flat_value(self) -> bool:
"""Return whether the Lstr is a 'flat' value. """Return whether the Lstr is a 'flat' value.
This is defined as a simple string value incorporating no translations, This is defined as a simple string value incorporating no
resources, or substitutions. In this case it may be reasonable to translations, resources, or substitutions. In this case it may
replace it with a raw string value, perform string manipulation on it, be reasonable to replace it with a raw string value, perform
etc. string manipulation on it, etc.
""" """
return bool('v' in self.args and not self.args.get('s', [])) return bool('v' in self.args and not self.args.get('s', []))

View File

@ -28,7 +28,7 @@ if TYPE_CHECKING:
# Build number and version of the ballistica binary we expect to be # Build number and version of the ballistica binary we expect to be
# using. # using.
TARGET_BALLISTICA_BUILD = 21143 TARGET_BALLISTICA_BUILD = 21147
TARGET_BALLISTICA_VERSION = '1.7.21' TARGET_BALLISTICA_VERSION = '1.7.21'
_g_env_config: EnvConfig | None = None _g_env_config: EnvConfig | None = None

View File

@ -176,10 +176,9 @@ void BaseFeatureSet::StartApp() {
LogVersionInfo(); LogVersionInfo();
g_core->LifecycleLog("will read config"); // Read in ba.app.config for anyone who wants to start looking at it
// (though we don't explicitly ask anyone to apply it until later).
// Read the app config. python->ReadConfig();
g_base->python->ReadConfig();
// Allow our subsystems to start doing work in their own threads and // Allow our subsystems to start doing work in their own threads and
// communicating with other subsystems. Note that we may still want to run // communicating with other subsystems. Note that we may still want to run
@ -188,12 +187,8 @@ void BaseFeatureSet::StartApp() {
// devices with the logic thread before the logic thread applies the // devices with the logic thread before the logic thread applies the
// current config to them). // current config to them).
g_core->LifecycleLog("will call python on-main-thread");
python->OnMainThreadStartApp(); python->OnMainThreadStartApp();
g_core->LifecycleLog("will call logic on-main-thread");
logic->OnMainThreadStartApp(); logic->OnMainThreadStartApp();
g_core->LifecycleLog("will call graphics-server on-main-thread");
graphics_server->OnMainThreadStartApp(); graphics_server->OnMainThreadStartApp();
if (bg_dynamics_server) { if (bg_dynamics_server) {
bg_dynamics_server->OnMainThreadStartApp(); bg_dynamics_server->OnMainThreadStartApp();
@ -209,10 +204,14 @@ void BaseFeatureSet::StartApp() {
// if called early. // if called early.
app_running_ = true; app_running_ = true;
// As the last step of this phase, tell the logic thread to apply // As the last step of this phase, tell the logic thread to apply the app
// the app config which will kick off screen creation and otherwise // config which will kick off screen creation and otherwise get the ball
// get the ball rolling. // rolling.
logic->event_loop()->PushCall([this] { logic->ApplyAppConfig(); }); {
Python::ScopedInterpreterLock gil;
python->objs().Get(BasePython::ObjID::kPushApplyAppConfigCall).Call();
}
g_core->LifecycleLog("start-app end (main thread)"); g_core->LifecycleLog("start-app end (main thread)");
} }
@ -252,11 +251,6 @@ void BaseFeatureSet::set_app_mode(AppMode* mode) {
app_mode_->OnActivate(); app_mode_->OnActivate();
// Since app-modes will mostly become active after the initial global
// apply-app-config happens, we need to tell them to do so explicitly when
// they spin up.
app_mode_->DoApplyAppConfig();
// Let some stuff know. // Let some stuff know.
logic->OnAppModeChanged(); logic->OnAppModeChanged();
} catch (const Exception& exc) { } catch (const Exception& exc) {

View File

@ -1111,6 +1111,11 @@ void Graphics::BuildAndPushFrameDef() {
assert(g_base->InLogicThread()); assert(g_base->InLogicThread());
assert(camera_.Exists()); assert(camera_.Exists());
// Keep track of when we're in here; can be useful for making sure stuff
// doesn't muck with our lists/etc. while we're using them.
assert(!building_frame_def_);
building_frame_def_ = true;
// We should not be building/pushing any frames until after // We should not be building/pushing any frames until after
// app-launch-commands have been run. // app-launch-commands have been run.
BA_PRECONDITION_FATAL(g_base->logic->app_bootstrapping_complete()); BA_PRECONDITION_FATAL(g_base->logic->app_bootstrapping_complete());
@ -1234,6 +1239,9 @@ void Graphics::BuildAndPushFrameDef() {
blotch_soft_verts_.clear(); blotch_soft_verts_.clear();
blotch_soft_obj_indices_.clear(); blotch_soft_obj_indices_.clear();
blotch_soft_obj_verts_.clear(); blotch_soft_obj_verts_.clear();
assert(building_frame_def_);
building_frame_def_ = false;
} }
void Graphics::DrawBoxingGlovesTest(FrameDef* frame_def) { void Graphics::DrawBoxingGlovesTest(FrameDef* frame_def) {
@ -1541,7 +1549,6 @@ void Graphics::SetSupportsHighQualityGraphics(bool s) {
void Graphics::ClearScreenMessageTranslations() { void Graphics::ClearScreenMessageTranslations() {
assert(g_base && g_base->InLogicThread()); assert(g_base && g_base->InLogicThread());
for (auto&& i : screen_messages_) { for (auto&& i : screen_messages_) {
i.translation_dirty = true; i.translation_dirty = true;
} }
@ -2017,6 +2024,10 @@ auto Graphics::ReflectionTypeFromString(const std::string& s)
void Graphics::LanguageChanged() { void Graphics::LanguageChanged() {
assert(g_base && g_base->InLogicThread()); assert(g_base && g_base->InLogicThread());
if (building_frame_def_) {
Log(LogLevel::kWarning,
"Graphics::LanguageChanged() called during draw; should not happen.");
}
// Also clear translations on all screen-messages. // Also clear translations on all screen-messages.
ClearScreenMessageTranslations(); ClearScreenMessageTranslations();
} }

View File

@ -432,6 +432,7 @@ class Graphics {
int64_t frame_def_count_{1}; int64_t frame_def_count_{1};
bool gyro_enabled_{true}; bool gyro_enabled_{true};
millisecs_t last_suppress_gyro_time_{}; millisecs_t last_suppress_gyro_time_{};
int building_frame_def_{};
}; };
} // namespace ballistica::base } // namespace ballistica::base

View File

@ -25,12 +25,9 @@ Logic::Logic() : display_timers_(new TimerList()) {
} }
void Logic::OnMainThreadStartApp() { void Logic::OnMainThreadStartApp() {
g_core->LifecycleLog("will create logic loop");
event_loop_ = new EventLoop(EventLoopID::kLogic); event_loop_ = new EventLoop(EventLoopID::kLogic);
g_core->pausable_event_loops.push_back(event_loop_); g_core->pausable_event_loops.push_back(event_loop_);
g_core->LifecycleLog("will push logic on-app-start");
// Sit and wait for our logic thread to run its startup stuff. // Sit and wait for our logic thread to run its startup stuff.
event_loop_->PushCallSynchronous([this] { OnAppStart(); }); event_loop_->PushCallSynchronous([this] { OnAppStart(); });
} }
@ -144,9 +141,8 @@ void Logic::OnAppShutdown() {
[] { g_base->app->LogicThreadShutdownComplete(); }); [] { g_base->app->LogicThreadShutdownComplete(); });
} }
void Logic::ApplyAppConfig() { void Logic::DoApplyAppConfig() {
assert(g_base->InLogicThread()); assert(g_base->InLogicThread());
g_core->LifecycleLog("apply-app-config");
// Give all our other subsystems a chance. // Give all our other subsystems a chance.
// Note: keep these in the same order as OnAppStart. // Note: keep these in the same order as OnAppStart.

View File

@ -33,7 +33,7 @@ class Logic {
void OnAppModeChanged(); void OnAppModeChanged();
void ApplyAppConfig(); void DoApplyAppConfig();
void OnScreenSizeChange(float virtual_width, float virtual_height, void OnScreenSizeChange(float virtual_width, float virtual_height,
float pixel_width, float pixel_height); float pixel_width, float pixel_height);

View File

@ -103,6 +103,7 @@ class BasePython {
kOpenURLWithWebBrowserModuleCall, kOpenURLWithWebBrowserModuleCall,
kOnNativeModuleImportCall, kOnNativeModuleImportCall,
kSetupEnvForAppRunCall, kSetupEnvForAppRunCall,
kPushApplyAppConfigCall,
kLast // Sentinel; must be at end. kLast // Sentinel; must be at end.
}; };

View File

@ -552,24 +552,22 @@ static PyMethodDef PyQuitDef = {
// ----------------------------- apply_config ---------------------------------- // ----------------------------- apply_config ----------------------------------
static auto PyApplyConfig(PyObject* self, PyObject* args) -> PyObject* { static auto PyDoApplyAppConfig(PyObject* self, PyObject* args) -> PyObject* {
BA_PYTHON_TRY; BA_PYTHON_TRY;
// Hmm; Python runs in the logic thread; technically we could just run BA_PRECONDITION(g_base->InLogicThread());
// ApplyAppConfig() immediately (though pushing is probably safer). g_base->logic->DoApplyAppConfig();
g_base->logic->event_loop()->PushCall(
[] { g_base->logic->ApplyAppConfig(); });
Py_RETURN_NONE; Py_RETURN_NONE;
BA_PYTHON_CATCH; BA_PYTHON_CATCH;
} }
static PyMethodDef PyApplyConfigDef = { static PyMethodDef PyDoApplyAppConfigDef = {
"apply_config", // name "do_apply_app_config", // name
PyApplyConfig, // method PyDoApplyAppConfig, // method
METH_VARARGS, // flags METH_VARARGS, // flags
"apply_config() -> None\n" "do_apply_app_config() -> None\n"
"\n" "\n"
"(internal)", "(internal)",
}; };
@ -1468,7 +1466,7 @@ auto PythonMethodsApp::GetMethods() -> std::vector<PyMethodDef> {
PyEnvDef, PyEnvDef,
PyPreEnvDef, PyPreEnvDef,
PyCommitConfigDef, PyCommitConfigDef,
PyApplyConfigDef, PyDoApplyAppConfigDef,
PyQuitDef, PyQuitDef,
PyAppTimerDef, PyAppTimerDef,
PyAppTimeDef, PyAppTimeDef,

View File

@ -1170,6 +1170,7 @@ static auto PySetInternalLanguageKeys(PyObject* self, PyObject* args)
BA_PRECONDITION(PyList_Check(random_names_list_obj)); BA_PRECONDITION(PyList_Check(random_names_list_obj));
std::unordered_map<std::string, std::string> language; std::unordered_map<std::string, std::string> language;
int size = static_cast<int>(PyList_GET_SIZE(list_obj)); int size = static_cast<int>(PyList_GET_SIZE(list_obj));
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
PyObject* entry = PyList_GET_ITEM(list_obj, i); PyObject* entry = PyList_GET_ITEM(list_obj, i);
if (!PyTuple_Check(entry) || PyTuple_GET_SIZE(entry) != 2 if (!PyTuple_Check(entry) || PyTuple_GET_SIZE(entry) != 2
@ -1180,6 +1181,7 @@ static auto PySetInternalLanguageKeys(PyObject* self, PyObject* args)
language[PyUnicode_AsUTF8(PyTuple_GET_ITEM(entry, 0))] = language[PyUnicode_AsUTF8(PyTuple_GET_ITEM(entry, 0))] =
PyUnicode_AsUTF8(PyTuple_GET_ITEM(entry, 1)); PyUnicode_AsUTF8(PyTuple_GET_ITEM(entry, 1));
} }
size = static_cast<int>(PyList_GET_SIZE(random_names_list_obj)); size = static_cast<int>(PyList_GET_SIZE(random_names_list_obj));
std::list<std::string> random_names; std::list<std::string> random_names;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
@ -1189,6 +1191,7 @@ static auto PySetInternalLanguageKeys(PyObject* self, PyObject* args)
} }
random_names.emplace_back(PyUnicode_AsUTF8(entry)); random_names.emplace_back(PyUnicode_AsUTF8(entry));
} }
Utils::SetRandomNameList(random_names); Utils::SetRandomNameList(random_names);
assert(g_base->logic); assert(g_base->logic);
g_base->assets->SetLanguageKeys(language); g_base->assets->SetLanguageKeys(language);

View File

@ -27,7 +27,7 @@ void CorePython::InitPython() {
// Pre-config as isolated if we include our own Python and as standard // Pre-config as isolated if we include our own Python and as standard
// otherwise. // otherwise.
PyPreConfig preconfig; PyPreConfig preconfig{};
if (g_buildconfig.contains_python_dist()) { if (g_buildconfig.contains_python_dist()) {
PyPreConfig_InitIsolatedConfig(&preconfig); PyPreConfig_InitIsolatedConfig(&preconfig);
} else { } else {
@ -44,7 +44,7 @@ void CorePython::InitPython() {
// Configure as isolated if we include our own Python and as standard // Configure as isolated if we include our own Python and as standard
// otherwise. // otherwise.
PyConfig config; PyConfig config{};
if (g_buildconfig.contains_python_dist()) { if (g_buildconfig.contains_python_dist()) {
PyConfig_InitIsolatedConfig(&config); PyConfig_InitIsolatedConfig(&config);
// We manage paths 100% ourself in this case and don't want any site // We manage paths 100% ourself in this case and don't want any site
@ -113,6 +113,7 @@ void CorePython::InitPython() {
// Init Python. // Init Python.
status = Py_InitializeFromConfig(&config); status = Py_InitializeFromConfig(&config);
BA_PRECONDITION_FATAL(!PyStatus_Exception(status)); BA_PRECONDITION_FATAL(!PyStatus_Exception(status));
PyConfig_Clear(&config);
} }
void CorePython::EnablePythonLoggingCalls() { void CorePython::EnablePythonLoggingCalls() {

View File

@ -77,7 +77,14 @@ bool SceneV1AppMode::InMainMenu() const {
static SceneV1AppMode* g_scene_v1_app_mode{}; static SceneV1AppMode* g_scene_v1_app_mode{};
void SceneV1AppMode::OnActivate() { Reset(); } void SceneV1AppMode::OnActivate() {
Reset();
// To set initial states, explicitly fire some of our 'On-Foo-Changed'
// callbacks.
DoApplyAppConfig();
LanguageChanged();
}
void SceneV1AppMode::OnAppStart() { assert(g_base->InLogicThread()); } void SceneV1AppMode::OnAppStart() { assert(g_base->InLogicThread()); }
@ -1197,6 +1204,7 @@ void SceneV1AppMode::PruneSessions() {
} }
void SceneV1AppMode::LanguageChanged() { void SceneV1AppMode::LanguageChanged() {
assert(g_base && g_base->InLogicThread());
if (Session* session = GetForegroundSession()) { if (Session* session = GetForegroundSession()) {
session->LanguageChanged(); session->LanguageChanged();
} }

View File

@ -39,7 +39,7 @@ auto main(int argc, char** argv) -> int {
namespace ballistica { namespace ballistica {
// These are set automatically via script; don't modify them here. // These are set automatically via script; don't modify them here.
const int kEngineBuildNumber = 21143; const int kEngineBuildNumber = 21147;
const char* kEngineVersion = "1.7.21"; const char* kEngineVersion = "1.7.21";
auto MonolithicMain(const core::CoreConfig& core_config) -> int { auto MonolithicMain(const core::CoreConfig& core_config) -> int {

View File

@ -76,16 +76,9 @@ EventLoop::EventLoop(EventLoopID identifier_in, ThreadSource source)
// Block until the thread is bootstrapped. // Block until the thread is bootstrapped.
// (maybe not necessary, but let's be cautious in case we'd // (maybe not necessary, but let's be cautious in case we'd
// try to use things like thread_id before they're known). // try to use things like thread_id before they're known).
if (identifier_ == EventLoopID::kLogic) {
g_core->LifecycleLog("logic thread bootstrap wait begin");
}
std::unique_lock lock(client_listener_mutex_); std::unique_lock lock(client_listener_mutex_);
client_listener_cv_.wait(lock, [this] { return bootstrapped_; }); client_listener_cv_.wait(lock, [this] { return bootstrapped_; });
if (identifier_ == EventLoopID::kLogic) {
g_core->LifecycleLog("logic thread bootstrap wait end");
}
break; break;
} }
case ThreadSource::kWrapMain: { case ThreadSource::kWrapMain: {
@ -512,7 +505,7 @@ void EventLoop::PushThreadMessage(const ThreadMessage& t) {
// So tally up any logs and send them after. // So tally up any logs and send them after.
std::vector<std::pair<LogLevel, std::string>> log_entries; std::vector<std::pair<LogLevel, std::string>> log_entries;
{ {
std::unique_lock<std::mutex> lock(thread_message_mutex_); std::unique_lock lock(thread_message_mutex_);
// Plop the data on to the list; we're assuming the mutex is locked. // Plop the data on to the list; we're assuming the mutex is locked.
thread_messages_.push_back(t); thread_messages_.push_back(t);
@ -742,11 +735,11 @@ auto EventLoop::CheckPushSafety() -> bool {
} }
} }
auto EventLoop::CheckPushRunnableSafety() -> bool { auto EventLoop::CheckPushRunnableSafety() -> bool {
std::scoped_lock lock(client_listener_mutex_); std::unique_lock lock(thread_message_mutex_);
// We first complain when we get to 1000 queued messages so // We first complain when we get to 1000 queued messages so
// let's consider things unsafe when we're halfway there. // let's consider things unsafe when we're halfway there.
return (thread_messages_.size() < kThreadMessageSafetyThreshold); return thread_messages_.size() < kThreadMessageSafetyThreshold;
} }
} // namespace ballistica } // namespace ballistica

View File

@ -12,4 +12,5 @@ values = [
babase.app.handle_deep_link, # kDeepLinkCall babase.app.handle_deep_link, # kDeepLinkCall
babase.app.lang.get_resource, # kGetResourceCall babase.app.lang.get_resource, # kGetResourceCall
babase.app.lang.translate, # kTranslateCall babase.app.lang.translate, # kTranslateCall
babase.app.push_apply_app_config, # kPushApplyAppConfigCall
] ]

View File

@ -18,7 +18,7 @@ if TYPE_CHECKING:
# Python version we build here (not necessarily same as we use in repo). # Python version we build here (not necessarily same as we use in repo).
PY_VER_ANDROID = '3.11' PY_VER_ANDROID = '3.11'
PY_VER_EXACT_ANDROID = '3.11.3' PY_VER_EXACT_ANDROID = '3.11.4'
PY_VER_APPLE = '3.11' PY_VER_APPLE = '3.11'
PY_VER_EXACT_APPLE = '3.11.3' PY_VER_EXACT_APPLE = '3.11.3'