From c13b937c799ed675ef2645d4d5f40f0274807d64 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 26 Jun 2023 13:16:38 -0700 Subject: [PATCH] subsystems cleanup --- .efrocachemap | 150 ++++----- CHANGELOG.md | 9 +- config/featuresets/README.md | 6 +- src/assets/ba_data/python/babase/_app.py | 40 ++- .../ba_data/python/babase/_appconfig.py | 10 +- .../ba_data/python/babase/_appsubsystem.py | 3 + src/assets/ba_data/python/babase/_language.py | 292 +++++++++--------- src/assets/ba_data/python/baenv.py | 2 +- src/ballistica/base/base.cc | 28 +- src/ballistica/base/graphics/graphics.cc | 13 +- src/ballistica/base/graphics/graphics.h | 1 + src/ballistica/base/logic/logic.cc | 6 +- src/ballistica/base/logic/logic.h | 2 +- src/ballistica/base/python/base_python.h | 1 + .../base/python/methods/python_methods_app.cc | 20 +- .../python/methods/python_methods_misc.cc | 3 + src/ballistica/core/python/core_python.cc | 5 +- .../scene_v1/support/scene_v1_app_mode.cc | 10 +- src/ballistica/shared/ballistica.cc | 2 +- .../shared/foundation/event_loop.cc | 13 +- .../babasemeta/pyembed/binding_base_app.py | 1 + tools/efrotools/pybuild.py | 2 +- 22 files changed, 339 insertions(+), 280 deletions(-) diff --git a/.efrocachemap b/.efrocachemap index 78274bd8..7cebddc4 100644 --- a/.efrocachemap +++ b/.efrocachemap @@ -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/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/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/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", @@ -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/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/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/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/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/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", @@ -2598,20 +2598,20 @@ "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/_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_arm-linux-androideabi.py": "https://files.ballistica.net/cache/ba1/ad/f1/41818ec9b47e366cd72361921dbc", - "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_x86_64-linux-android.py": "https://files.ballistica.net/cache/ba1/5b/ee/5962b42890b6758cea433e67376d", - "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_arm-linux-androideabi.py": "https://files.ballistica.net/cache/ba1/37/a5/4b085a7f6cdfa03b3c6c887979ba", - "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_x86_64-linux-android.py": "https://files.ballistica.net/cache/ba1/81/1d/8202416f4ba308e6a011139115e6", + "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/50/6b/3106270f7f7ae33e195d1f5caa55", + "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/d6/7f/056bcaf3d491d68254c206067d19", + "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/a4/d1/4baa8cfc0019363742349449942b", + "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/bf/6c/e8f2788661352014e973351f35cf", "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/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/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/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", @@ -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/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/selector_events.py": "https://files.ballistica.net/cache/ba1/f7/ee/7809d8b807ccda22db7870f508ac", - "build/assets/pylib-android/asyncio/sslproto.py": "https://files.ballistica.net/cache/ba1/3a/63/9532c92e12c9bf06fd131a6fc419", + "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/2e/c1/b21e523055147d94c8c634154aab", "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/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/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/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/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_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/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/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/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", @@ -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/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/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/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", @@ -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/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/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/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", @@ -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/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/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/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/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/_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/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", @@ -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/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/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/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", @@ -2911,7 +2911,7 @@ "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/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/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", @@ -2920,27 +2920,27 @@ "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/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/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/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/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/pdb.py": "https://files.ballistica.net/cache/ba1/da/4c/484348d4b2009479dadd878f20b0", + "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/11/7b/0d24ccb89edc5f183c94f6722f70", "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/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/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/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/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/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/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", @@ -2960,7 +2960,7 @@ "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/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/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", @@ -2980,12 +2980,12 @@ "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/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/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/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/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", @@ -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/_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/trace.py": "https://files.ballistica.net/cache/ba1/c8/60/164b4f4317299db04e29070a163d", - "build/assets/pylib-android/traceback.py": "https://files.ballistica.net/cache/ba1/bb/2d/c63a0bcd438697904573807bd392", + "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/91/f6/7818e621e3b2f5bf583ed6863ef8", "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/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/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/request.py": "https://files.ballistica.net/cache/ba1/e8/9a/853d76344f1ca197926d49b3b9e6", + "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/09/b8/666fe8319542f7247bb9f8573806", "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/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/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/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", @@ -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/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/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/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", @@ -3064,7 +3064,7 @@ "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/_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_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", @@ -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_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_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_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", @@ -4068,26 +4068,26 @@ "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/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/release/ballisticakit": "https://files.ballistica.net/cache/ba1/ea/05/7ea22ed9c5f1f514b9e39d90cde6", - "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/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/40/b8/fa9aeee2292fde22215797faa75b", - "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/release/ballisticakit": "https://files.ballistica.net/cache/ba1/a1/85/9ca2209cd2baf659b4e2b40b79fd", - "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/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/e7/b8/53a5666e993663054202393e589b", - "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/8f/6a/9b8d0b4b6dd97f4d5d4b48f5b84d", - "build/prefab/full/mac_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/48/1d/c9762e55356a288aebfefecbd43e", - "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/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/78/71/d5ab66d0f577a746e05f44d753bd", - "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/release/ballisticakit": "https://files.ballistica.net/cache/ba1/85/ba/98322913ba70e71e3858ee7916e5", - "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/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/9f/b7/fdec2c3c131abe175e716698605e", - "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/08/59/d701c12712effe14b89ea37c013f", - "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/99/01/8c49ac6d34dc7219ccf047794437", - "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/release/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/32/18/35d3b785547afc61cfb6b3ba3504", + "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/ae/11/e739a151be864da4c87c15c155fa", + "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/32/e1/c608186cfa17d5ebc93e676166bd", + "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/f3/a9/1b6cce29b356c2317b6f1126ba9b", + "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/22/c0/4b32851978bf97cccd4c45f71a43", + "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/3d/c5/ffa0a2051e467d002bc272316da6", + "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/62/8f/4ffeb86197612d84f648141e5dd4", + "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/c0/d6/18ad1186b589cd30d0f252b0b542", + "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/75/c2/8f3635450156521401ff37c6070c", + "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/bf/1e/d1bf7b259d98fd464889aa755c22", + "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/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/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", @@ -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_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/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/5c/4e/26f72e68d55dcf12d62b1274b7ad", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/c3/61/d40dfab20f59da7155b942632066", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/a9/f7/ddae4d7e7e59f0b9db4eee09d226", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/4a/a4/c9c09853640eb5a5f5a0ce4cf71c", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/9d/42/a7bc0e69180e2e45e5b5b93cc49c", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/9a/a7/5e31d7b317e4544feff2eabad4a5", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/3b/74/84e7f0fb207da9a99a0c71b12a2e", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/2e/dc/4406fedd025a80037e395097ee19", + "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/56/b0/7ce3384cfb63d3a224e317c69fa8", + "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/dc/e6/e052a9115ca83686c6634d2c26ff", + "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/3a/d7/a0329b38db9b594c36ff88a884c9", + "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/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/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_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/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", diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cfc12d7..3fb4b892 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. +- 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) @@ -360,8 +363,8 @@ 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' to enable debug logging. -- (build 21125) Fixed a bug where feature-sets would not have their - DoApplyConfig callbacks called in C++, which was causing the server-mode +- (build 21125) Fixed a bug where app-modes would not have their + DoApplyAppConfig callbacks called in C++, which was causing the server-mode `idle_exit_minutes` value to be ignored. Servers should now properly exit after being idle for this length of time. - (build 21126) Reworked the efrocache system used by public builds for diff --git a/config/featuresets/README.md b/config/featuresets/README.md index 2fec0f7e..e3af368f 100644 --- a/config/featuresets/README.md +++ b/config/featuresets/README.md @@ -21,14 +21,14 @@ with specific naming conventions: in this directory called `featureset_foo_bar.py`. - **Python Package**: If feature set `foo_bar` provides a Python package, it 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++ 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, code or data used to generate other source code), it should be a directory 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 be a directory named `test_foo_bar` ('test_' prefix, unmodified feature set name) under [tests](../../tests). diff --git a/src/assets/ba_data/python/babase/_app.py b/src/assets/ba_data/python/babase/_app.py index 3eaaf6a3..06e89e6f 100644 --- a/src/assets/ba_data/python/babase/_app.py +++ b/src/assets/ba_data/python/babase/_app.py @@ -54,8 +54,8 @@ class App: # pylint: disable=too-many-public-methods plugins: PluginSubsystem + lang: LanguageSubsystem - # log_handler: LogHandler health_monitor: AppHealthMonitor class State(Enum): @@ -247,6 +247,7 @@ class App: self._called_on_app_running = False self._app_paused = False self._subsystem_registration_ended = False + self._pending_apply_app_config = False # Config. self.config_file_healthy = False @@ -281,7 +282,6 @@ class App: self.components = AppComponentSubsystem() self.meta = MetadataSubsystem() - self.lang = LanguageSubsystem() self.net = NetworkSubsystem() self.workspaces = WorkspaceSubsystem() self._pending_intent: AppIntent | None = None @@ -297,7 +297,7 @@ class App: self._asyncio_timer: babase.AppTimer | None = 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': return @@ -306,6 +306,7 @@ class App: # some of this stuff accesses babase.app and that doesn't # exist yet as of our __init__() call. + self.lang = LanguageSubsystem() self.plugins = PluginSubsystem() def register_subsystem(self, subsystem: AppSubsystem) -> None: @@ -622,6 +623,39 @@ class App: # plugin hasn't already told it to do something. 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): """Decides which app modes to use to handle intents. diff --git a/src/assets/ba_data/python/babase/_appconfig.py b/src/assets/ba_data/python/babase/_appconfig.py index 06f26ab3..8edb454a 100644 --- a/src/assets/ba_data/python/babase/_appconfig.py +++ b/src/assets/ba_data/python/babase/_appconfig.py @@ -10,6 +10,8 @@ import _babase if TYPE_CHECKING: from typing import Any +_g_pending_apply = False # pylint: disable=invalid-name + class AppConfig(dict): """A special dict that holds the game's persistent configuration values. @@ -74,8 +76,12 @@ class AppConfig(dict): return _babase.get_appconfig_builtin_keys() def apply(self) -> None: - """Apply config values to the running app.""" - _babase.apply_config() + """Apply config values to the running app. + + 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: """Commits the config to local storage. diff --git a/src/assets/ba_data/python/babase/_appsubsystem.py b/src/assets/ba_data/python/babase/_appsubsystem.py index 97f08b2b..6bee6f97 100644 --- a/src/assets/ba_data/python/babase/_appsubsystem.py +++ b/src/assets/ba_data/python/babase/_appsubsystem.py @@ -47,3 +47,6 @@ class AppSubsystem: def on_app_shutdown(self) -> None: """Called when the app is shutting down.""" + + def do_apply_app_config(self) -> None: + """Called when the app config should be applied.""" diff --git a/src/assets/ba_data/python/babase/_language.py b/src/assets/ba_data/python/babase/_language.py index a61acbe2..0466c707 100644 --- a/src/assets/ba_data/python/babase/_language.py +++ b/src/assets/ba_data/python/babase/_language.py @@ -9,6 +9,7 @@ import logging from typing import TYPE_CHECKING, overload import _babase +from babase._appsubsystem import AppSubsystem if TYPE_CHECKING: from typing import Any, Sequence @@ -16,44 +17,21 @@ if TYPE_CHECKING: import babase -class LanguageSubsystem: - """Wraps up language related app functionality. +class LanguageSubsystem(AppSubsystem): + """Language functionality for the app. 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: - self.language_target: AttrDict | None = None - self.language_merged: AttrDict | None = None - self.default_language = self._get_default_language() + super().__init__() + self.default_language: str = self._get_default_language() - 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 + self._language: str | None = None + self._language_target: AttrDict | None = None + self._language_merged: AttrDict | None = None @property def locale(self) -> str: @@ -67,62 +45,16 @@ class LanguageSubsystem: assert isinstance(env['locale'], str) 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 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 - automatically based on babase.App.locale or other factors. + automatically based on locale or other factors. """ - assert isinstance(_babase.app.config, dict) - return _babase.app.config.get('Lang', self.default_language) + if self._language is None: + raise RuntimeError('App language is not yet set.') + return self._language @property def available_languages(self) -> list[str]: @@ -164,13 +96,14 @@ class LanguageSubsystem: print_change: bool = True, store_to_config: bool = True, ) -> None: - """Set the active language used for the game. + """Set the active app language. Pass None to use OS default language. """ # pylint: disable=too-many-locals # pylint: disable=too-many-statements # pylint: disable=too-many-branches + assert _babase.in_logic_thread() cfg = _babase.app.config cur_language = cfg.get('Lang', None) @@ -215,38 +148,36 @@ class LanguageSubsystem: with open(lmodfile, encoding='utf-8') as infile: lmodvalues = json.loads(infile.read()) except Exception: - from babase import _error - - _error.print_exception('Exception importing language:', language) + logging.exception("Error importing language '%s'.", language) _babase.screenmessage( - "Error setting language to '" - + language - + "'; see log for details", + f"Error setting language to '{language}'; see log for details.", color=(1, 0, 0), ) switched = False lmodvalues = None + self._language = language + # Create an attrdict of *just* our target language. - self.language_target = AttrDict() - langtarget = self.language_target + self._language_target = AttrDict() + langtarget = self._language_target assert langtarget is not None _add_to_attr_dict( langtarget, lmodvalues if lmodvalues is not None else lenglishvalues ) - # Create an attrdict of our target language overlaid - # on our base (english). + # Create an attrdict of our target language overlaid on our base + # (english). languages = [lenglishvalues] if lmodvalues is not None: languages.append(lmodvalues) lfull = AttrDict() for lmod in languages: _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; - # start with everything in their 'internal' section. + # Pass some keys/values in for low level code to use; start with + # everything in their 'internal' section. internal_vals = [ v for v in list(lfull['internal'].items()) if isinstance(v[1], str) ] @@ -265,7 +196,7 @@ class LanguageSubsystem: ('axisText', lfull['configGamepadWindow']['axisText']) ) internal_vals.append(('buttonText', lfull['buttonText'])) - lmerged = self.language_merged + lmerged = self._language_merged assert lmerged is not None random_names = [ n.strip() for n in lmerged['randomPlayerNamesText'].split(',') @@ -283,6 +214,13 @@ class LanguageSubsystem: 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( self, resource: str, @@ -294,38 +232,30 @@ class LanguageSubsystem: DEPRECATED; use babase.Lstr functionality for these purposes. """ try: - # If we have no language set, go ahead and set it. - if self.language_merged is None: - language = self.language + # If we have no language set, try and set it to english. + # Also make a fuss because we should try to avoid this. + if self._language_merged is None: try: + if _babase.do_once(): + logging.warning( + 'get_resource() called before language' + ' set; falling back to english.' + ) self.setlanguage( - language, print_change=False, store_to_config=False + 'English', print_change=False, store_to_config=False ) except Exception: - from babase import _error - - logging.exception('Error setting language to %s.', language) - - # 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' - ) + logging.exception( + 'Error setting fallback english language.' + ) + raise # If they provided a fallback_resource value, try the - # target-language-only dict first and then fall back to trying the - # fallback_resource value in the merged dict. + # target-language-only dict first and then fall back to + # trying the fallback_resource value in the merged dict. if fallback_resource is not None: try: - values = self.language_target + values = self._language_target splits = resource.split('.') dicts = splits[:-1] key = splits[-1] @@ -336,11 +266,11 @@ class LanguageSubsystem: val = values[key] return val except Exception: - # FIXME: Shouldn't we try the fallback resource in the - # merged dict AFTER we try the main resource in the - # merged dict? + # FIXME: Shouldn't we try the fallback resource in + # the merged dict AFTER we try the main resource in + # the merged dict? try: - values = self.language_merged + values = self._language_merged splits = fallback_resource.split('.') dicts = splits[:-1] key = splits[-1] @@ -352,14 +282,15 @@ class LanguageSubsystem: return val except Exception: - # If we got nothing for fallback_resource, default - # to the normal code which checks or primary - # value in the merge dict; there's a chance we can - # get an english value for it (which we weren't - # looking for the first time through). + # If we got nothing for fallback_resource, + # default to the normal code which checks or + # primary value in the merge dict; there's a + # chance we can get an english value for it + # (which we weren't looking for the first time + # through). pass - values = self.language_merged + values = self._language_merged splits = resource.split('.') dicts = splits[:-1] key = splits[-1] @@ -371,9 +302,9 @@ class LanguageSubsystem: return val except Exception: - # Ok, looks like we couldn't find our main or fallback resource - # anywhere. Now if we've been given a fallback value, return it; - # otherwise fail. + # Ok, looks like we couldn't find our main or fallback + # resource anywhere. Now if we've been given a fallback + # value, return it; otherwise fail. from babase import _error if fallback_value is not None: @@ -426,18 +357,93 @@ class LanguageSubsystem: raise ValueError('Invalid Input; must be length 1') 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: """Used to define strings in a language-independent way. Category: **General Utility Classes** - These should be used whenever possible in place of hard-coded strings - so that in-game or UI elements show up correctly on all clients in their - currently-active language. + These should be used whenever possible in place of hard-coded + strings so that in-game or UI elements show up correctly on all + clients in their currently-active language. - To see available resource keys, look at any of the bs_language_*.py files - in the game or the translations pages at legacy.ballistica.net/translate. + To see available resource keys, look at any of the bs_language_*.py + files in the game or the translations pages at + legacy.ballistica.net/translate. ##### Examples EXAMPLE 1: specify a string from a resource path @@ -573,10 +579,10 @@ class Lstr: def is_flat_value(self) -> bool: """Return whether the Lstr is a 'flat' value. - This is defined as a simple string value incorporating no translations, - resources, or substitutions. In this case it may be reasonable to - replace it with a raw string value, perform string manipulation on it, - etc. + This is defined as a simple string value incorporating no + translations, resources, or substitutions. In this case it may + be reasonable to replace it with a raw string value, perform + string manipulation on it, etc. """ return bool('v' in self.args and not self.args.get('s', [])) diff --git a/src/assets/ba_data/python/baenv.py b/src/assets/ba_data/python/baenv.py index f8aba30e..dfa06e89 100644 --- a/src/assets/ba_data/python/baenv.py +++ b/src/assets/ba_data/python/baenv.py @@ -28,7 +28,7 @@ if TYPE_CHECKING: # Build number and version of the ballistica binary we expect to be # using. -TARGET_BALLISTICA_BUILD = 21143 +TARGET_BALLISTICA_BUILD = 21147 TARGET_BALLISTICA_VERSION = '1.7.21' _g_env_config: EnvConfig | None = None diff --git a/src/ballistica/base/base.cc b/src/ballistica/base/base.cc index a50607c5..8e422976 100644 --- a/src/ballistica/base/base.cc +++ b/src/ballistica/base/base.cc @@ -176,10 +176,9 @@ void BaseFeatureSet::StartApp() { LogVersionInfo(); - g_core->LifecycleLog("will read config"); - - // Read the app config. - g_base->python->ReadConfig(); + // 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). + python->ReadConfig(); // Allow our subsystems to start doing work in their own threads and // 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 // current config to them). - g_core->LifecycleLog("will call python on-main-thread"); - python->OnMainThreadStartApp(); - g_core->LifecycleLog("will call logic on-main-thread"); logic->OnMainThreadStartApp(); - g_core->LifecycleLog("will call graphics-server on-main-thread"); graphics_server->OnMainThreadStartApp(); if (bg_dynamics_server) { bg_dynamics_server->OnMainThreadStartApp(); @@ -209,10 +204,14 @@ void BaseFeatureSet::StartApp() { // if called early. app_running_ = true; - // As the last step of this phase, tell the logic thread to apply - // the app config which will kick off screen creation and otherwise - // get the ball rolling. - logic->event_loop()->PushCall([this] { logic->ApplyAppConfig(); }); + // As the last step of this phase, tell the logic thread to apply the app + // config which will kick off screen creation and otherwise get the ball + // rolling. + { + Python::ScopedInterpreterLock gil; + python->objs().Get(BasePython::ObjID::kPushApplyAppConfigCall).Call(); + } + g_core->LifecycleLog("start-app end (main thread)"); } @@ -252,11 +251,6 @@ void BaseFeatureSet::set_app_mode(AppMode* mode) { 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. logic->OnAppModeChanged(); } catch (const Exception& exc) { diff --git a/src/ballistica/base/graphics/graphics.cc b/src/ballistica/base/graphics/graphics.cc index da62114f..57e10ec4 100644 --- a/src/ballistica/base/graphics/graphics.cc +++ b/src/ballistica/base/graphics/graphics.cc @@ -1111,6 +1111,11 @@ void Graphics::BuildAndPushFrameDef() { assert(g_base->InLogicThread()); 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 // app-launch-commands have been run. BA_PRECONDITION_FATAL(g_base->logic->app_bootstrapping_complete()); @@ -1234,6 +1239,9 @@ void Graphics::BuildAndPushFrameDef() { blotch_soft_verts_.clear(); blotch_soft_obj_indices_.clear(); blotch_soft_obj_verts_.clear(); + + assert(building_frame_def_); + building_frame_def_ = false; } void Graphics::DrawBoxingGlovesTest(FrameDef* frame_def) { @@ -1541,7 +1549,6 @@ void Graphics::SetSupportsHighQualityGraphics(bool s) { void Graphics::ClearScreenMessageTranslations() { assert(g_base && g_base->InLogicThread()); - for (auto&& i : screen_messages_) { i.translation_dirty = true; } @@ -2017,6 +2024,10 @@ auto Graphics::ReflectionTypeFromString(const std::string& s) void Graphics::LanguageChanged() { 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. ClearScreenMessageTranslations(); } diff --git a/src/ballistica/base/graphics/graphics.h b/src/ballistica/base/graphics/graphics.h index 8bdbde54..adc46b47 100644 --- a/src/ballistica/base/graphics/graphics.h +++ b/src/ballistica/base/graphics/graphics.h @@ -432,6 +432,7 @@ class Graphics { int64_t frame_def_count_{1}; bool gyro_enabled_{true}; millisecs_t last_suppress_gyro_time_{}; + int building_frame_def_{}; }; } // namespace ballistica::base diff --git a/src/ballistica/base/logic/logic.cc b/src/ballistica/base/logic/logic.cc index ee201df3..9cb2f4a6 100644 --- a/src/ballistica/base/logic/logic.cc +++ b/src/ballistica/base/logic/logic.cc @@ -25,12 +25,9 @@ Logic::Logic() : display_timers_(new TimerList()) { } void Logic::OnMainThreadStartApp() { - g_core->LifecycleLog("will create logic loop"); - event_loop_ = new EventLoop(EventLoopID::kLogic); 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. event_loop_->PushCallSynchronous([this] { OnAppStart(); }); } @@ -144,9 +141,8 @@ void Logic::OnAppShutdown() { [] { g_base->app->LogicThreadShutdownComplete(); }); } -void Logic::ApplyAppConfig() { +void Logic::DoApplyAppConfig() { assert(g_base->InLogicThread()); - g_core->LifecycleLog("apply-app-config"); // Give all our other subsystems a chance. // Note: keep these in the same order as OnAppStart. diff --git a/src/ballistica/base/logic/logic.h b/src/ballistica/base/logic/logic.h index 3fa62f75..ae0a15e7 100644 --- a/src/ballistica/base/logic/logic.h +++ b/src/ballistica/base/logic/logic.h @@ -33,7 +33,7 @@ class Logic { void OnAppModeChanged(); - void ApplyAppConfig(); + void DoApplyAppConfig(); void OnScreenSizeChange(float virtual_width, float virtual_height, float pixel_width, float pixel_height); diff --git a/src/ballistica/base/python/base_python.h b/src/ballistica/base/python/base_python.h index 6691337b..b9b8df23 100644 --- a/src/ballistica/base/python/base_python.h +++ b/src/ballistica/base/python/base_python.h @@ -103,6 +103,7 @@ class BasePython { kOpenURLWithWebBrowserModuleCall, kOnNativeModuleImportCall, kSetupEnvForAppRunCall, + kPushApplyAppConfigCall, kLast // Sentinel; must be at end. }; diff --git a/src/ballistica/base/python/methods/python_methods_app.cc b/src/ballistica/base/python/methods/python_methods_app.cc index f8c49c96..d4c121b7 100644 --- a/src/ballistica/base/python/methods/python_methods_app.cc +++ b/src/ballistica/base/python/methods/python_methods_app.cc @@ -552,24 +552,22 @@ static PyMethodDef PyQuitDef = { // ----------------------------- apply_config ---------------------------------- -static auto PyApplyConfig(PyObject* self, PyObject* args) -> PyObject* { +static auto PyDoApplyAppConfig(PyObject* self, PyObject* args) -> PyObject* { BA_PYTHON_TRY; - // Hmm; Python runs in the logic thread; technically we could just run - // ApplyAppConfig() immediately (though pushing is probably safer). - g_base->logic->event_loop()->PushCall( - [] { g_base->logic->ApplyAppConfig(); }); + BA_PRECONDITION(g_base->InLogicThread()); + g_base->logic->DoApplyAppConfig(); Py_RETURN_NONE; BA_PYTHON_CATCH; } -static PyMethodDef PyApplyConfigDef = { - "apply_config", // name - PyApplyConfig, // method - METH_VARARGS, // flags +static PyMethodDef PyDoApplyAppConfigDef = { + "do_apply_app_config", // name + PyDoApplyAppConfig, // method + METH_VARARGS, // flags - "apply_config() -> None\n" + "do_apply_app_config() -> None\n" "\n" "(internal)", }; @@ -1468,7 +1466,7 @@ auto PythonMethodsApp::GetMethods() -> std::vector { PyEnvDef, PyPreEnvDef, PyCommitConfigDef, - PyApplyConfigDef, + PyDoApplyAppConfigDef, PyQuitDef, PyAppTimerDef, PyAppTimeDef, diff --git a/src/ballistica/base/python/methods/python_methods_misc.cc b/src/ballistica/base/python/methods/python_methods_misc.cc index 250408b5..8a19a481 100644 --- a/src/ballistica/base/python/methods/python_methods_misc.cc +++ b/src/ballistica/base/python/methods/python_methods_misc.cc @@ -1170,6 +1170,7 @@ static auto PySetInternalLanguageKeys(PyObject* self, PyObject* args) BA_PRECONDITION(PyList_Check(random_names_list_obj)); std::unordered_map language; int size = static_cast(PyList_GET_SIZE(list_obj)); + for (int i = 0; i < size; i++) { PyObject* entry = PyList_GET_ITEM(list_obj, i); 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))] = PyUnicode_AsUTF8(PyTuple_GET_ITEM(entry, 1)); } + size = static_cast(PyList_GET_SIZE(random_names_list_obj)); std::list random_names; 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)); } + Utils::SetRandomNameList(random_names); assert(g_base->logic); g_base->assets->SetLanguageKeys(language); diff --git a/src/ballistica/core/python/core_python.cc b/src/ballistica/core/python/core_python.cc index bd764788..9e27bcea 100644 --- a/src/ballistica/core/python/core_python.cc +++ b/src/ballistica/core/python/core_python.cc @@ -27,7 +27,7 @@ void CorePython::InitPython() { // Pre-config as isolated if we include our own Python and as standard // otherwise. - PyPreConfig preconfig; + PyPreConfig preconfig{}; if (g_buildconfig.contains_python_dist()) { PyPreConfig_InitIsolatedConfig(&preconfig); } else { @@ -44,7 +44,7 @@ void CorePython::InitPython() { // Configure as isolated if we include our own Python and as standard // otherwise. - PyConfig config; + PyConfig config{}; if (g_buildconfig.contains_python_dist()) { PyConfig_InitIsolatedConfig(&config); // We manage paths 100% ourself in this case and don't want any site @@ -113,6 +113,7 @@ void CorePython::InitPython() { // Init Python. status = Py_InitializeFromConfig(&config); BA_PRECONDITION_FATAL(!PyStatus_Exception(status)); + PyConfig_Clear(&config); } void CorePython::EnablePythonLoggingCalls() { diff --git a/src/ballistica/scene_v1/support/scene_v1_app_mode.cc b/src/ballistica/scene_v1/support/scene_v1_app_mode.cc index bfb2cb3c..e241eb02 100644 --- a/src/ballistica/scene_v1/support/scene_v1_app_mode.cc +++ b/src/ballistica/scene_v1/support/scene_v1_app_mode.cc @@ -77,7 +77,14 @@ bool SceneV1AppMode::InMainMenu() const { 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()); } @@ -1197,6 +1204,7 @@ void SceneV1AppMode::PruneSessions() { } void SceneV1AppMode::LanguageChanged() { + assert(g_base && g_base->InLogicThread()); if (Session* session = GetForegroundSession()) { session->LanguageChanged(); } diff --git a/src/ballistica/shared/ballistica.cc b/src/ballistica/shared/ballistica.cc index 6977cc48..c753211e 100644 --- a/src/ballistica/shared/ballistica.cc +++ b/src/ballistica/shared/ballistica.cc @@ -39,7 +39,7 @@ auto main(int argc, char** argv) -> int { namespace ballistica { // 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"; auto MonolithicMain(const core::CoreConfig& core_config) -> int { diff --git a/src/ballistica/shared/foundation/event_loop.cc b/src/ballistica/shared/foundation/event_loop.cc index 1a285600..96f1d04c 100644 --- a/src/ballistica/shared/foundation/event_loop.cc +++ b/src/ballistica/shared/foundation/event_loop.cc @@ -76,16 +76,9 @@ EventLoop::EventLoop(EventLoopID identifier_in, ThreadSource source) // Block until the thread is bootstrapped. // (maybe not necessary, but let's be cautious in case we'd // 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_); client_listener_cv_.wait(lock, [this] { return bootstrapped_; }); - if (identifier_ == EventLoopID::kLogic) { - g_core->LifecycleLog("logic thread bootstrap wait end"); - } - break; } case ThreadSource::kWrapMain: { @@ -512,7 +505,7 @@ void EventLoop::PushThreadMessage(const ThreadMessage& t) { // So tally up any logs and send them after. std::vector> log_entries; { - std::unique_lock 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. thread_messages_.push_back(t); @@ -742,11 +735,11 @@ auto EventLoop::CheckPushSafety() -> 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 // let's consider things unsafe when we're halfway there. - return (thread_messages_.size() < kThreadMessageSafetyThreshold); + return thread_messages_.size() < kThreadMessageSafetyThreshold; } } // namespace ballistica diff --git a/src/meta/babasemeta/pyembed/binding_base_app.py b/src/meta/babasemeta/pyembed/binding_base_app.py index 23cd5c97..a35b4065 100644 --- a/src/meta/babasemeta/pyembed/binding_base_app.py +++ b/src/meta/babasemeta/pyembed/binding_base_app.py @@ -12,4 +12,5 @@ values = [ babase.app.handle_deep_link, # kDeepLinkCall babase.app.lang.get_resource, # kGetResourceCall babase.app.lang.translate, # kTranslateCall + babase.app.push_apply_app_config, # kPushApplyAppConfigCall ] diff --git a/tools/efrotools/pybuild.py b/tools/efrotools/pybuild.py index f011b82e..734455ba 100644 --- a/tools/efrotools/pybuild.py +++ b/tools/efrotools/pybuild.py @@ -18,7 +18,7 @@ if TYPE_CHECKING: # Python version we build here (not necessarily same as we use in repo). 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_EXACT_APPLE = '3.11.3'