diff --git a/.efrocachemap b/.efrocachemap
index 20386dd3..d33e32cc 100644
--- a/.efrocachemap
+++ b/.efrocachemap
@@ -421,10 +421,10 @@
"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/7a/57/60b86e6ab02f04afe4af903cf6cb",
+ "build/assets/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/a1/f9/645b8c7e1e99dd11446bc77005da",
"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",
+ "build/assets/ba_data/data/languages/chinese.json": "https://files.ballistica.net/cache/ba1/b6/00/924583b899165757f412eef0dd01",
"build/assets/ba_data/data/languages/chinesetraditional.json": "https://files.ballistica.net/cache/ba1/50/4c/4fb39f065b1a2f0320026a2e1b92",
"build/assets/ba_data/data/languages/croatian.json": "https://files.ballistica.net/cache/ba1/76/65/32c67af5bd0144c2d63cab0516fa",
"build/assets/ba_data/data/languages/czech.json": "https://files.ballistica.net/cache/ba1/f3/ce/219840946cb8f9aa6d3e25927ab3",
@@ -434,30 +434,30 @@
"build/assets/ba_data/data/languages/esperanto.json": "https://files.ballistica.net/cache/ba1/0e/39/7cfa5f3fb8cef5f4a64f21cda880",
"build/assets/ba_data/data/languages/filipino.json": "https://files.ballistica.net/cache/ba1/cb/49/1739273c68c82cebca0aee16d6c9",
"build/assets/ba_data/data/languages/french.json": "https://files.ballistica.net/cache/ba1/51/89/e01389f8153497b56fbf0fa069c2",
- "build/assets/ba_data/data/languages/german.json": "https://files.ballistica.net/cache/ba1/22/a4/452043a401252ca66b703ce5d4aa",
+ "build/assets/ba_data/data/languages/german.json": "https://files.ballistica.net/cache/ba1/54/97/54d2a530d825200c6126be56df5c",
"build/assets/ba_data/data/languages/gibberish.json": "https://files.ballistica.net/cache/ba1/23/6f/8547ba09722b7c7f5b8333986984",
"build/assets/ba_data/data/languages/greek.json": "https://files.ballistica.net/cache/ba1/a6/5d/78f912e9a89f98de004405167a6a",
"build/assets/ba_data/data/languages/hindi.json": "https://files.ballistica.net/cache/ba1/88/ee/0cda537bab9ac827def5e236fe1a",
"build/assets/ba_data/data/languages/hungarian.json": "https://files.ballistica.net/cache/ba1/00/ba/cf1b8bb9f7914f64647d4665b0a8",
- "build/assets/ba_data/data/languages/indonesian.json": "https://files.ballistica.net/cache/ba1/0d/59/18149137b31721efe131d89689c6",
- "build/assets/ba_data/data/languages/italian.json": "https://files.ballistica.net/cache/ba1/ac/03/2e4a2322a96e3d6d5e2aa3ae327d",
+ "build/assets/ba_data/data/languages/indonesian.json": "https://files.ballistica.net/cache/ba1/58/3b/ae1ecc04375cee089a82359110b7",
+ "build/assets/ba_data/data/languages/italian.json": "https://files.ballistica.net/cache/ba1/67/44/40ada7b8e76adceb2129d7668df6",
"build/assets/ba_data/data/languages/korean.json": "https://files.ballistica.net/cache/ba1/bd/c1/3f8632adda5517059323d928f192",
"build/assets/ba_data/data/languages/malay.json": "https://files.ballistica.net/cache/ba1/83/25/62ce997fc70704b9234c95fb2e38",
- "build/assets/ba_data/data/languages/persian.json": "https://files.ballistica.net/cache/ba1/f4/1d/3524c00d2e8e8c4613ebdf1493b4",
+ "build/assets/ba_data/data/languages/persian.json": "https://files.ballistica.net/cache/ba1/0e/46/0cb71876e02d361e11db64640831",
"build/assets/ba_data/data/languages/polish.json": "https://files.ballistica.net/cache/ba1/a6/a7/4a9a289fa1b97847c9a2578c112b",
"build/assets/ba_data/data/languages/portuguese.json": "https://files.ballistica.net/cache/ba1/99/b2/7c598c90fd522132af3536aef0ee",
"build/assets/ba_data/data/languages/romanian.json": "https://files.ballistica.net/cache/ba1/ae/eb/dd54f65939c2facc6ac50c117826",
- "build/assets/ba_data/data/languages/russian.json": "https://files.ballistica.net/cache/ba1/9a/c0/a2ff20e53f7d22037a9c8211a362",
+ "build/assets/ba_data/data/languages/russian.json": "https://files.ballistica.net/cache/ba1/aa/99/f9f597787fe4e09c8ab53fe2e081",
"build/assets/ba_data/data/languages/serbian.json": "https://files.ballistica.net/cache/ba1/d7/45/2dd72ac0e51680cb39b5ebaa1c69",
"build/assets/ba_data/data/languages/slovak.json": "https://files.ballistica.net/cache/ba1/27/96/2d53dc3f7dd4e877cd40faafeeef",
- "build/assets/ba_data/data/languages/spanish.json": "https://files.ballistica.net/cache/ba1/f2/7a/b0eb0a0b68e25d3a3fb1ec436645",
+ "build/assets/ba_data/data/languages/spanish.json": "https://files.ballistica.net/cache/ba1/bd/5e/80c74f96bb50d270396d437d6750",
"build/assets/ba_data/data/languages/swedish.json": "https://files.ballistica.net/cache/ba1/77/d6/71f10613291ebf9c71da66f18a18",
"build/assets/ba_data/data/languages/tamil.json": "https://files.ballistica.net/cache/ba1/c7/fc/5ed7bd686839ec1a867763248cf9",
"build/assets/ba_data/data/languages/thai.json": "https://files.ballistica.net/cache/ba1/33/f6/3753c9af9a5b238d229a0bf23fbc",
- "build/assets/ba_data/data/languages/turkish.json": "https://files.ballistica.net/cache/ba1/e2/6a/07fa9a25878ee89807ce9962edf9",
+ "build/assets/ba_data/data/languages/turkish.json": "https://files.ballistica.net/cache/ba1/0a/97/f1f948f6587ea7d40b639aba67ce",
"build/assets/ba_data/data/languages/ukrainian.json": "https://files.ballistica.net/cache/ba1/97/4a/399422e3061fdd82f66591283397",
"build/assets/ba_data/data/languages/venetian.json": "https://files.ballistica.net/cache/ba1/e4/74/5c85bc56487bb715712c8b90a1eb",
- "build/assets/ba_data/data/languages/vietnamese.json": "https://files.ballistica.net/cache/ba1/54/44/fd36756645564c42bb597de6377e",
+ "build/assets/ba_data/data/languages/vietnamese.json": "https://files.ballistica.net/cache/ba1/92/1c/d1e50f60fe3e101f246e172750ba",
"build/assets/ba_data/data/maps/big_g.json": "https://files.ballistica.net/cache/ba1/1d/d3/01d490643088a435ce75df971054",
"build/assets/ba_data/data/maps/bridgit.json": "https://files.ballistica.net/cache/ba1/6a/ea/74805f4880cc11237c5734a24422",
"build/assets/ba_data/data/maps/courtyard.json": "https://files.ballistica.net/cache/ba1/4b/83/6554c8949bcd2ae382f5e3c1a9cc",
@@ -950,7 +950,7 @@
"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/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/11/61/88cec81052958f4f98239d4bf3ca",
+ "build/assets/ba_data/python-site-packages/typing_extensions.py": "https://files.ballistica.net/cache/ba1/08/4d/93bb609d798a3930dfb5e25eba59",
"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",
@@ -1535,10 +1535,10 @@
"build/assets/ba_data/textures/fontExtras.dds": "https://files.ballistica.net/cache/ba1/7a/b1/1df1b3a3daa651dfad34219b89f5",
"build/assets/ba_data/textures/fontExtras.ktx": "https://files.ballistica.net/cache/ba1/30/c3/c8ca2cdf1209ff177017bb10f0a8",
"build/assets/ba_data/textures/fontExtras.pvr": "https://files.ballistica.net/cache/ba1/fd/3b/0bd902c30e4b7aa5fe00e1eec4be",
- "build/assets/ba_data/textures/fontExtras2.dds": "https://files.ballistica.net/cache/ba1/82/c4/9af5f739a4be74084c8b98a892b0",
- "build/assets/ba_data/textures/fontExtras2.ktx": "https://files.ballistica.net/cache/ba1/77/f7/55e108b8b910890c14cd58445d73",
- "build/assets/ba_data/textures/fontExtras2.pvr": "https://files.ballistica.net/cache/ba1/f7/ea/f0f444e39269dbe1f2af1bff310b",
- "build/assets/ba_data/textures/fontExtras2_preview.png": "https://files.ballistica.net/cache/ba1/f2/7c/50b10075a47e994651f71362c31c",
+ "build/assets/ba_data/textures/fontExtras2.dds": "https://files.ballistica.net/cache/ba1/18/06/3a12912dadc9528afd90d1cf2369",
+ "build/assets/ba_data/textures/fontExtras2.ktx": "https://files.ballistica.net/cache/ba1/36/da/7f6cfbfb8d32fb14371de0a8f660",
+ "build/assets/ba_data/textures/fontExtras2.pvr": "https://files.ballistica.net/cache/ba1/7a/4e/8e64ac05313b1782fb5b958150d0",
+ "build/assets/ba_data/textures/fontExtras2_preview.png": "https://files.ballistica.net/cache/ba1/f5/bc/05c8c34cb3ff7284e9edab7b8bcd",
"build/assets/ba_data/textures/fontExtras3.dds": "https://files.ballistica.net/cache/ba1/4f/ce/73998583207fd7692a816e3d86b0",
"build/assets/ba_data/textures/fontExtras3.ktx": "https://files.ballistica.net/cache/ba1/13/d6/1e663088bc97c1fa906a46ed5955",
"build/assets/ba_data/textures/fontExtras3.pvr": "https://files.ballistica.net/cache/ba1/56/ba/c0d5f8672f36e1408a1e57cdd3d3",
@@ -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/d3/9c/74d2e9a1b887acd46f621272efa3",
- "build/prefab/full/linux_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/7d/41/d4590a21449f3b210a706ceb381a",
- "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/11/f3/83f0fba3740203bae12c710ad8ff",
- "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/47/d5/0a6181c67777beef34a67fb36ba6",
- "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/50/be/3b2dc25c0768e711a482d132a3ba",
- "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/91/c2/9d5d5278575f0bd6d3a056fb8160",
- "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/34/6a/5cd3c0ab5a433ea77b7c9a047ae4",
- "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/5f/da/617750d459fe6218dc3f7f860cff",
- "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/95/48/47d550a802e5712719b7c5cda899",
- "build/prefab/full/mac_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/39/a3/c0c54265ea7c9debb317efc782a4",
- "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/bc/1d/a15d7691a7c52b37b8ac3cebd4be",
- "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/58/22/9552473e93773ba12a8ba0e33ac8",
- "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/47/62/078ec6167c01d3b767a2babb0b73",
- "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/e0/eb/dabceff6aed25203fd9e973b29b7",
- "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/98/ac/f5b5872699c4e04a6d6a97077d53",
- "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/3c/ef/49bb8c6f2f1bf9114ab0c42e700c",
- "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/25/7e/795d70e09b4fa86df5a9e0c55c1f",
- "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/cf/97/df4d783c76f152c2ea2153a2b36c",
- "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/d3/8a/8188e90299eec825be4ad76c6a9a",
- "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/93/16/bd9d50566e7f666a07f42ca9ea9a",
+ "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/59/35/c9ba716b720aedc7d54e9c83fdc4",
+ "build/prefab/full/linux_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/f8/58/e1200d725edb25877ddb03beb5ea",
+ "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/7b/2f/61d28afa0a9675add2c51e6dfd00",
+ "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/41/b8/6b4a0592bcf8c92aa2d1412a7288",
+ "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/0f/62/44f31d9bc7547754a8c79ec17141",
+ "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/e1/85/b3dd2968bd85c2e0c449fe036111",
+ "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/60/89/7d935c2046c494e588ce6761125e",
+ "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/06/e4/4a46b2659ff56e018190240de396",
+ "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/c4/0d/50883b77f756641fbd80092d35b7",
+ "build/prefab/full/mac_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/34/2f/a6deb8563e4cd29dff8f11c788bb",
+ "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/e7/cc/431b21630eb325f068d472dad763",
+ "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/b9/b9/33463ca1f078854d2e015df65b66",
+ "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/e9/80/b1483328b1ae0876fa63dbde529b",
+ "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/9f/19/9ed55ca8ed416273415024a58191",
+ "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/d6/08/e08e3380f18c5dd476ad0bf082f3",
+ "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/e4/6a/525b520d455f042bff170f663319",
+ "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/bb/22/2f948b1a6b5aac27e91f40ee6f06",
+ "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/4e/8c/f9b308b586a926bf855dba2e2509",
+ "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/4f/22/3a06c12dac1628f0761afb421122",
+ "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/b9/3a/e6a05acad4ea2325df5eae6c3fc8",
"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,16 +4104,16 @@
"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/1f/12/f5811bdd6c1b81af2c79d14aac0f",
- "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/70/05/940d32b8f297caac70e5edd23247",
- "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/7d/3e/5022438c01db4b47cc8f2c5d8cfd",
- "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/f0/c4/d6ea86c3dba824a5e03b4cbd7d43",
- "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/4c/06/8a43c496f1e0e8c44b85e581fa55",
- "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/af/06/51d246eb5b5c4a677155edd00cce",
- "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/05/c1/ca3bce6340742a8bdb526b440df5",
- "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/5f/91/6c36a3587d3e2682287bc36b4e1f",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/de/7c/be80b73558c5ce768b0084926910",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/e5/a1/644901f5d239aeab9fb976f79ffd",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/33/6e/5d5eb648ccb1646eaef4c626caa8",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/7a/1f/24348f323cfe76d907ef9d4b554e",
+ "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/95/0b/2bd52e8669d06f0a6729f1c2d8e0",
+ "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/a2/41/48a2c40099e3a27f283b3de9d5b5",
+ "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/e7/af/912d52ca415fe16b893eb4b92e70",
+ "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/d4/63/45858118d88aca3ed4f3bfec9cca",
"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/f8/cd/3af311ac63147882590123b78318",
"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/c2/f2/cb8d052d76a1f4abe163cab2276e",
"src/ballistica/classic/mgen/pyembed/binding_classic.inc": "https://files.ballistica.net/cache/ba1/3c/eb/412513963f0818ab39c58bf292e3",
diff --git a/.idea/dictionaries/ericf.xml b/.idea/dictionaries/ericf.xml
index e4b43ccb..508e635b 100644
--- a/.idea/dictionaries/ericf.xml
+++ b/.idea/dictionaries/ericf.xml
@@ -422,6 +422,7 @@
cbresults
cbtn
cbtnoffs
+ cbuf
ccfgs
ccind
ccode
@@ -939,6 +940,7 @@
expectedsig
explodable
explodey
+ explodinary
exportlist
exportoptions
exportoptionspath
@@ -1861,6 +1863,7 @@
namecap
namedarg
namel
+ namepre
nametext
nameu
nameval
@@ -2867,6 +2870,7 @@
termcolors
termios
testbuffer
+ testbuild
testcall
testcallable
testcapi
@@ -3190,6 +3194,7 @@
wintdir
wintype
wmsbe
+ wonkiness
wonkypaths
wonkysuffix
woohoo
@@ -3201,6 +3206,7 @@
woutdir
wpath
wprjp
+ wreadlink
wref
writeauxiliaryfile
writeclasses
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ff2ab847..5fc13f71 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,10 +1,16 @@
-### 1.7.22 (build 21156, api 8, 2023-07-08)
+### 1.7.22 (build 21159, api 8, 2023-07-10)
- Fixed a very rare race condition when launching threads or sending synchronous
cross-thread messages. This was manifesting as one out of several thousand
server launches hanging.
- Changed health box from a red cross to a green cross (turns out games aren't
supposed to use red crosses for health for legal reasons).
+- Cleaned up how Android sets up its OpenGL context; it should be more flexible
+ with the config formats it allows may might fix rare cases of graphics setup
+ failing (such as with latest Android emulator for me). Please holler if you
+ see any graphics wonkiness with this update.
+- Added SoK's explodinary icon to the game's custom text drawing because SoK is
+ awesome.
### 1.7.21 (build 21152, api 8, 2023-06-27)
diff --git a/ballisticakit-cmake/.idea/dictionaries/ericf.xml b/ballisticakit-cmake/.idea/dictionaries/ericf.xml
index 42a1ee62..6d589261 100644
--- a/ballisticakit-cmake/.idea/dictionaries/ericf.xml
+++ b/ballisticakit-cmake/.idea/dictionaries/ericf.xml
@@ -277,6 +277,7 @@
cbgn
cbresults
cbtnoffs
+ cbuf
ccdd
ccind
ccontext
@@ -562,6 +563,7 @@
expbool
expectedsig
expl
+ explodinary
exportlist
exporttypestr
expsrc
@@ -1071,6 +1073,7 @@
mywidget
namecap
namel
+ namepre
nameu
nameval
nbuffer
@@ -1648,6 +1651,7 @@
templatefs
tempvec
tenum
+ testbuild
testclinic
testint
testinternalcapi
@@ -1841,6 +1845,7 @@
worldspace
woutdir
wprjp
+ wreadlink
writeauxiliaryfile
wspath
wsroot
diff --git a/src/assets/ba_data/python/baenv.py b/src/assets/ba_data/python/baenv.py
index 168f3af6..d1e26f6a 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 = 21156
+TARGET_BALLISTICA_BUILD = 21159
TARGET_BALLISTICA_VERSION = '1.7.22'
_g_env_config: EnvConfig | None = None
diff --git a/src/assets/ba_data/python/bauiv1lib/profile/edit.py b/src/assets/ba_data/python/bauiv1lib/profile/edit.py
index a61e137b..ffa08732 100644
--- a/src/assets/ba_data/python/bauiv1lib/profile/edit.py
+++ b/src/assets/ba_data/python/bauiv1lib/profile/edit.py
@@ -525,9 +525,9 @@ class EditProfileWindow(bui.Window):
bui.SpecialChar.GAME_CIRCLE_LOGO,
bui.SpecialChar.OUYA_LOGO,
bui.SpecialChar.LOCAL_ACCOUNT,
- bui.SpecialChar.ALIBABA_LOGO,
bui.SpecialChar.OCULUS_LOGO,
bui.SpecialChar.NVIDIA_LOGO,
+ bui.SpecialChar.V2_LOGO,
]
]
)
diff --git a/src/ballistica/base/assets/assets.cc b/src/ballistica/base/assets/assets.cc
index 72bb7686..1b57aedf 100644
--- a/src/ballistica/base/assets/assets.cc
+++ b/src/ballistica/base/assets/assets.cc
@@ -1259,7 +1259,7 @@ void Assets::InitSpecialChars() {
special_char_strings_[SpecialChar::kTrophy0b] = "\xee\x80\xAE";
special_char_strings_[SpecialChar::kTrophy4] = "\xee\x80\xAF";
special_char_strings_[SpecialChar::kLocalAccount] = "\xee\x80\xB0";
- special_char_strings_[SpecialChar::kAlibabaLogo] = "\xee\x80\xB1";
+ special_char_strings_[SpecialChar::kExplodinaryLogo] = "\xee\x80\xB1";
special_char_strings_[SpecialChar::kFlagUnitedStates] = "\xee\x80\xB2";
special_char_strings_[SpecialChar::kFlagMexico] = "\xee\x80\xB3";
diff --git a/src/ballistica/base/input/device/input_device.cc b/src/ballistica/base/input/device/input_device.cc
index 51ae3b86..a86a227a 100644
--- a/src/ballistica/base/input/device/input_device.cc
+++ b/src/ballistica/base/input/device/input_device.cc
@@ -56,18 +56,30 @@ void InputDevice::RequestPlayer() {
last_input_time_millisecs_ =
static_cast(g_base->logic->display_time() * 1000.0);
+ // Tracking down a bug.
+ BA_PRECONDITION_FATAL(delegate_.Exists());
delegate_->RequestPlayer();
}
// If we're attached to a remote player, ship completed packets every now and
// then.
-void InputDevice::Update() { delegate_->Update(); }
+void InputDevice::Update() {
+ // Tracking down a bug.
+ BA_PRECONDITION_FATAL(delegate_.Exists());
+ delegate_->Update();
+}
auto InputDevice::AttachedToPlayer() const -> bool {
+ // Tracking down a bug.
+ BA_PRECONDITION_FATAL(delegate_.Exists());
return delegate_->AttachedToPlayer();
}
-void InputDevice::DetachFromPlayer() { delegate_->DetachFromPlayer(); }
+void InputDevice::DetachFromPlayer() {
+ // Tracking down a bug.
+ BA_PRECONDITION_FATAL(delegate_.Exists());
+ delegate_->DetachFromPlayer();
+}
void InputDevice::UpdateLastInputTime() {
// Keep our own individual time, and also let
@@ -83,6 +95,8 @@ void InputDevice::InputCommand(InputType type, float value) {
// Make note that we're being used in some way.
UpdateLastInputTime();
+ // Tracking down a bug.
+ BA_PRECONDITION_FATAL(delegate_.Exists());
delegate_->InputCommand(type, value);
}
diff --git a/src/ballistica/base/input/device/input_device_delegate.h b/src/ballistica/base/input/device/input_device_delegate.h
index a908e20a..94437962 100644
--- a/src/ballistica/base/input/device/input_device_delegate.h
+++ b/src/ballistica/base/input/device/input_device_delegate.h
@@ -44,7 +44,10 @@ class InputDeviceDelegate : public Object {
/// An input-device-delegate should never outlive its input_device;
/// our accessor returns a reference to show this does not need
/// to be checked.
- auto input_device() const -> InputDevice& { return *input_device_; }
+ auto input_device() const -> InputDevice& {
+ BA_PRECONDITION_FATAL(input_device_.Exists());
+ return *input_device_;
+ }
void set_input_device(InputDevice* device);
auto InputDeviceExists() const -> bool { return input_device_.Exists(); }
diff --git a/src/ballistica/core/platform/core_platform.cc b/src/ballistica/core/platform/core_platform.cc
index 1e20ba1f..59163621 100644
--- a/src/ballistica/core/platform/core_platform.cc
+++ b/src/ballistica/core/platform/core_platform.cc
@@ -83,6 +83,15 @@
#error no BA_PLATFORM_CLASS defined for this platform
#endif
+// A call that can be used by custom built native libraries (Python, etc.)
+// to forward along debug messages to us.
+// FIXME: Reconcile this with our existing C++ version. This one does not
+// require the engine to be spun up so it better suited for things like
+// debugging native libs.
+extern "C" {
+void BallisticaLowLevelDebugLog(const char* msg) {}
+}
+
namespace ballistica::core {
auto CorePlatform::Create() -> CorePlatform* {
diff --git a/src/ballistica/core/python/core_python.cc b/src/ballistica/core/python/core_python.cc
index 9e27bcea..1e022af9 100644
--- a/src/ballistica/core/python/core_python.cc
+++ b/src/ballistica/core/python/core_python.cc
@@ -10,6 +10,11 @@
namespace ballistica::core {
+static void LowLevelPythonDebugLog(const char* msg) {
+ assert(g_core);
+ g_core->platform->DebugLog(msg);
+}
+
void CorePython::ApplyBaEnvConfig() {
// Fetch the env-config (creates it if need be).
auto envcfg = objs().Get(core::CorePython::ObjID::kBaEnvGetConfigCall).Call();
@@ -21,6 +26,11 @@ void CorePython::InitPython() {
assert(g_core->InMainThread());
assert(g_buildconfig.monolithic_build());
+ // Install our low level logger in our custom Python builds.
+#ifdef PY_HAVE_BALLISTICA_LOW_LEVEL_DEBUG_LOG
+ Py_BallisticaLowLevelDebugLog = LowLevelPythonDebugLog;
+#endif
+
// Flip on some extra runtime debugging options in debug builds.
// https://docs.python.org/3/library/devmode.html#devmode
int dev_mode{g_buildconfig.debug_build()};
diff --git a/src/ballistica/shared/ballistica.cc b/src/ballistica/shared/ballistica.cc
index 1a917447..3637157f 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 = 21156;
+const int kEngineBuildNumber = 21159;
const char* kEngineVersion = "1.7.22";
auto MonolithicMain(const core::CoreConfig& core_config) -> int {
@@ -63,6 +63,9 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int {
// import it first thing even if we don't explicitly use it.
l_core = core::CoreFeatureSet::Import(&core_config);
+ // TEMP - bug hunting.
+ l_core->platform->DebugLog("mm1");
+
// If a command was passed, simply run it and exit. We want to act
// simply as a Python interpreter in that case; we don't do any
// environment setup (aside from the bits core does automatically such
@@ -75,6 +78,9 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int {
exit(success ? 0 : 1);
}
+ // TEMP - bug hunting.
+ l_core->platform->DebugLog("mm2");
+
// Ok, looks like we're doing a standard monolithic-mode app run.
// -------------------------------------------------------------------------
@@ -87,6 +93,9 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int {
// those modules get loaded from in the first place.
l_core->python->MonolithicModeBaEnvConfigure();
+ // TEMP - bug hunting.
+ l_core->platform->DebugLog("mm3");
+
// We need the base feature-set to run a full app but we don't have a hard
// dependency to it. Let's see if it's available.
l_base = l_core->SoftImportBase();
@@ -98,6 +107,9 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int {
// Phase 2: "The pieces are moving."
// -------------------------------------------------------------------------
+ // TEMP - bug hunting.
+ l_core->platform->DebugLog("mm4");
+
// Spin up all app machinery such as threads and subsystems. This gets
// things ready to rock, but there's no actual rocking quite yet.
l_base->StartApp();
@@ -106,6 +118,9 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int {
// Phase 3: "We come to it at last; the great battle of our time."
// -------------------------------------------------------------------------
+ // TEMP - bug hunting.
+ l_core->platform->DebugLog("mm5");
+
// At this point we unleash the beast and then simply process events
// until the app exits (or we return from this function and let the
// environment do that part).
diff --git a/src/ballistica/shared/foundation/types.h b/src/ballistica/shared/foundation/types.h
index 3f8ea132..fc76099b 100644
--- a/src/ballistica/shared/foundation/types.h
+++ b/src/ballistica/shared/foundation/types.h
@@ -212,7 +212,7 @@ enum class SpecialChar {
kTrophy0b,
kTrophy4,
kLocalAccount,
- kAlibabaLogo,
+ kExplodinaryLogo,
kFlagUnitedStates,
kFlagMexico,
kFlagGermany,
diff --git a/tools/efro/log.py b/tools/efro/log.py
index 341fe54e..94b117b3 100644
--- a/tools/efro/log.py
+++ b/tools/efro/log.py
@@ -314,8 +314,7 @@ class LogHandler(logging.Handler):
# Called by logging to send us records.
- # Special case: filter out this common extra-chatty category.
- # TODO - perhaps should use a standard logging.Filter for this.
+ # TODO - kill this.
if (
self._suppress_non_root_debug
and record.name != 'root'
@@ -366,10 +365,14 @@ class LogHandler(logging.Handler):
# make tight debugging harder.
if self._echofile is not None:
ends = LEVELNO_COLOR_CODES.get(record.levelno)
+ namepre = (
+ f'{TerminalColor.WHITE.value}{record.name}:'
+ f'{TerminalColor.RESET.value} '
+ )
if ends is not None:
- self._echofile.write(f'{ends[0]}{msg}{ends[1]}\n')
+ self._echofile.write(f'{namepre}{ends[0]}{msg}{ends[1]}\n')
else:
- self._echofile.write(f'{msg}\n')
+ self._echofile.write(f'{namepre}{msg}\n')
self._echofile.flush()
if __debug__:
@@ -659,6 +662,9 @@ def setup_logging(
had_previous_handlers = bool(logging.root.handlers)
logging.basicConfig(
level=lmap[level],
+ # format='%(name)s: %(message)s',
+ # We dump *only* the message here. We pass various log record bits
+ # around and format things fancier where they end up.
format='%(message)s',
handlers=[loghandler],
force=True,
diff --git a/tools/efrotools/pybuild.py b/tools/efrotools/pybuild.py
index 734455ba..5c66cf20 100644
--- a/tools/efrotools/pybuild.py
+++ b/tools/efrotools/pybuild.py
@@ -646,7 +646,13 @@ def patch_modules_setup(python_dir: str, baseplatform: str) -> None:
def android_patch() -> None:
"""Run necessary patches on an android archive before building."""
patch_modules_setup('.', 'android')
- # _patch_setup_file('android', '?', '?')
+
+ # Add our low level debug call.
+ _patch_py_h()
+
+ # Use that call...
+ _patch_py_wreadlink_test()
+
# _patch_py_ssl()
@@ -704,6 +710,51 @@ def android_patch_ssl() -> None:
writefile(fname, txt)
+def _patch_py_wreadlink_test() -> None:
+ fname = 'Python/fileutils.c'
+ txt = readfile(fname)
+ txt = replace_exact(
+ txt,
+ " cbuf[res] = '\\0'; /* buf will be null terminated */",
+ (
+ ' char dlog[256];\n'
+ ' snprintf(dlog, sizeof(dlog), "hello world res=%d mpl=%d",'
+ ' (int)res, (int)MAXPATHLEN);\n'
+ ' Py_BallisticaLowLevelDebugLog(dlog);\n'
+ " cbuf[res] = '\\0'; /* buf will be null terminated */"
+ ),
+ )
+ writefile(fname, txt)
+
+
+def _patch_py_h() -> None:
+ fname = 'Include/fileutils.h'
+ txt = readfile(fname)
+ txt = replace_exact(
+ txt,
+ '\n#ifdef __cplusplus\n}\n',
+ (
+ '\n'
+ '/* ericf hack for debugging */\n'
+ '#define PY_HAVE_BALLISTICA_LOW_LEVEL_DEBUG_LOG\n'
+ 'extern void (*Py_BallisticaLowLevelDebugLog)(const char* msg);\n'
+ '\n'
+ '#ifdef __cplusplus\n}\n'
+ ),
+ )
+ writefile(fname, txt)
+
+ fname = 'Python/fileutils.c'
+ txt = readfile(fname)
+ txt = replace_exact(
+ txt,
+ ' _Py_END_SUPPRESS_IPH\n}',
+ ' _Py_END_SUPPRESS_IPH\n}\n\n'
+ 'void (*Py_BallisticaLowLevelDebugLog)(const char* msg) = NULL;\n',
+ )
+ writefile(fname, txt)
+
+
def _patch_py_ssl() -> None:
# UPDATE: this is now included in Python as of 3.10.6; woohoo!
if bool(True):