got command line args wired up on modular builds

This commit is contained in:
Eric 2023-07-27 21:39:17 -07:00
parent 7ed0abb708
commit d90b1093af
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
52 changed files with 793 additions and 564 deletions

108
.efrocachemap generated
View File

@ -421,9 +421,9 @@
"build/assets/ba_data/audio/zoeOw.ogg": "https://files.ballistica.net/cache/ba1/74/be/fe45a8417e95b6a2233c51992a26", "build/assets/ba_data/audio/zoeOw.ogg": "https://files.ballistica.net/cache/ba1/74/be/fe45a8417e95b6a2233c51992a26",
"build/assets/ba_data/audio/zoePickup01.ogg": "https://files.ballistica.net/cache/ba1/48/ab/8cddfcde36a750856f3f81dd20c8", "build/assets/ba_data/audio/zoePickup01.ogg": "https://files.ballistica.net/cache/ba1/48/ab/8cddfcde36a750856f3f81dd20c8",
"build/assets/ba_data/audio/zoeScream01.ogg": "https://files.ballistica.net/cache/ba1/2b/46/8aedfa8741090247f04eb9e6df55", "build/assets/ba_data/audio/zoeScream01.ogg": "https://files.ballistica.net/cache/ba1/2b/46/8aedfa8741090247f04eb9e6df55",
"build/assets/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/b8/5e/c8766634397fb77ae3a407c05d63", "build/assets/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/85/81/9425f358eafcd41e91fb999612ba",
"build/assets/ba_data/data/languages/arabic.json": "https://files.ballistica.net/cache/ba1/6f/38/958616d8cb85916aa8b2bcd84f63", "build/assets/ba_data/data/languages/arabic.json": "https://files.ballistica.net/cache/ba1/db/96/1f7fe0541a31880929e1c17ea957",
"build/assets/ba_data/data/languages/belarussian.json": "https://files.ballistica.net/cache/ba1/57/68/d03a19b9035cfae7cdc5377d889a", "build/assets/ba_data/data/languages/belarussian.json": "https://files.ballistica.net/cache/ba1/5e/37/3ddcfa6e1f771b74c02298a6599a",
"build/assets/ba_data/data/languages/chinese.json": "https://files.ballistica.net/cache/ba1/b6/00/924583b899165757f412eef0dd01", "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/3f/e9/60a8f0ca529aa57b4f9cb7385abc", "build/assets/ba_data/data/languages/chinesetraditional.json": "https://files.ballistica.net/cache/ba1/3f/e9/60a8f0ca529aa57b4f9cb7385abc",
"build/assets/ba_data/data/languages/croatian.json": "https://files.ballistica.net/cache/ba1/76/65/32c67af5bd0144c2d63cab0516fa", "build/assets/ba_data/data/languages/croatian.json": "https://files.ballistica.net/cache/ba1/76/65/32c67af5bd0144c2d63cab0516fa",
@ -432,30 +432,30 @@
"build/assets/ba_data/data/languages/dutch.json": "https://files.ballistica.net/cache/ba1/22/b4/4a33bf81142ba2befad14eb5746e", "build/assets/ba_data/data/languages/dutch.json": "https://files.ballistica.net/cache/ba1/22/b4/4a33bf81142ba2befad14eb5746e",
"build/assets/ba_data/data/languages/english.json": "https://files.ballistica.net/cache/ba1/d1/6e/8899211693c20d3b00fc198f58c6", "build/assets/ba_data/data/languages/english.json": "https://files.ballistica.net/cache/ba1/d1/6e/8899211693c20d3b00fc198f58c6",
"build/assets/ba_data/data/languages/esperanto.json": "https://files.ballistica.net/cache/ba1/0e/39/7cfa5f3fb8cef5f4a64f21cda880", "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/filipino.json": "https://files.ballistica.net/cache/ba1/58/f3/63cfd8a3ccf0c904ab753d95789b",
"build/assets/ba_data/data/languages/french.json": "https://files.ballistica.net/cache/ba1/51/89/e01389f8153497b56fbf0fa069c2", "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/54/97/54d2a530d825200c6126be56df5c", "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/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/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/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/a9/b5/10de2f3928d8c1f4887e0975743f", "build/assets/ba_data/data/languages/hungarian.json": "https://files.ballistica.net/cache/ba1/79/6a/290a8c44a1e7635208c2ff5fdc6e",
"build/assets/ba_data/data/languages/indonesian.json": "https://files.ballistica.net/cache/ba1/58/3b/ae1ecc04375cee089a82359110b7", "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/italian.json": "https://files.ballistica.net/cache/ba1/ce/99/027a5d1af17689b4311eafa5ff24",
"build/assets/ba_data/data/languages/korean.json": "https://files.ballistica.net/cache/ba1/bd/c1/3f8632adda5517059323d928f192", "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/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/0e/46/0cb71876e02d361e11db64640831", "build/assets/ba_data/data/languages/persian.json": "https://files.ballistica.net/cache/ba1/51/19/aec9cbb2f8d00f2afaccf5fd5410",
"build/assets/ba_data/data/languages/polish.json": "https://files.ballistica.net/cache/ba1/a6/a7/4a9a289fa1b97847c9a2578c112b", "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/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/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/aa/99/f9f597787fe4e09c8ab53fe2e081", "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/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/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/45/dd/ce6d9dd446293f5e0ae541f36943", "build/assets/ba_data/data/languages/spanish.json": "https://files.ballistica.net/cache/ba1/87/76/884442afd08b31364dc6a8b0cc16",
"build/assets/ba_data/data/languages/swedish.json": "https://files.ballistica.net/cache/ba1/77/d6/71f10613291ebf9c71da66f18a18", "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/tamil.json": "https://files.ballistica.net/cache/ba1/b9/d4/b4e107456ea6420ee0f9d9d7a03e",
"build/assets/ba_data/data/languages/thai.json": "https://files.ballistica.net/cache/ba1/33/f6/3753c9af9a5b238d229a0bf23fbc", "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/0a/97/f1f948f6587ea7d40b639aba67ce", "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/ukrainian.json": "https://files.ballistica.net/cache/ba1/f7/2e/b51abfbbb56e27866895d7e947d2",
"build/assets/ba_data/data/languages/venetian.json": "https://files.ballistica.net/cache/ba1/b0/e3/d73ccf96c5fa490a54f090ee77a5", "build/assets/ba_data/data/languages/venetian.json": "https://files.ballistica.net/cache/ba1/b0/e3/d73ccf96c5fa490a54f090ee77a5",
"build/assets/ba_data/data/languages/vietnamese.json": "https://files.ballistica.net/cache/ba1/92/1c/d1e50f60fe3e101f246e172750ba", "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/big_g.json": "https://files.ballistica.net/cache/ba1/1d/d3/01d490643088a435ce75df971054",
@ -4068,50 +4068,50 @@
"build/assets/windows/Win32/ucrtbased.dll": "https://files.ballistica.net/cache/ba1/2d/ef/5335207d41b21b9823f6805997f1", "build/assets/windows/Win32/ucrtbased.dll": "https://files.ballistica.net/cache/ba1/2d/ef/5335207d41b21b9823f6805997f1",
"build/assets/windows/Win32/vc_redist.x86.exe": "https://files.ballistica.net/cache/ba1/b0/8a/55e2e77623fe657bea24f223a3ae", "build/assets/windows/Win32/vc_redist.x86.exe": "https://files.ballistica.net/cache/ba1/b0/8a/55e2e77623fe657bea24f223a3ae",
"build/assets/windows/Win32/vcruntime140d.dll": "https://files.ballistica.net/cache/ba1/86/5b/2af4d1e26a1a8073c89acb06e599", "build/assets/windows/Win32/vcruntime140d.dll": "https://files.ballistica.net/cache/ba1/86/5b/2af4d1e26a1a8073c89acb06e599",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/6b/a1/5f560a97ab8641091343c6ee688b", "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/3a/79/8c3f90efa237a22e105a984ddc69",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/50/dd/86cbb96aca3a339318b00574b2db", "build/prefab/full/linux_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/3f/fe/4c9f0926b60c6a4f5253fde8bb35",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/6b/b4/65070558df0a917c9a1aac8bb280", "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/98/07/9edc3796de01efe4f2fc30d3fcc9",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/93/04/19410cb96b5c12fc2cd20dd9c099", "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/37/ba/ee706254c5a21f08b8d4c47bb6e2",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/36/69/25b4f3e931ff0add15a975383491", "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/1f/9a/33ed6f8e2b78af7b00b2727c6630",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/16/68/6011835e4db7927b26761847950b", "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/11/f3/0feed20f5d34c23d96b39e5702ae",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/11/bf/aa9df1fd5ae51e9b076a324d8e7a", "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/4d/f8/c574339560c3f324d258d73dbe70",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/8a/76/54da9b7ff4d79164d3f4dea2782b", "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/bc/b1/66680fead22bdecfcd4c8d825f8b",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/4a/08/bf75de3244efe6fc342139a6da32", "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/79/44/6cddf6fe194de66ab9576d1d958d",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/33/c7/d3534c1d605b5bcc4a541457cde9", "build/prefab/full/mac_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/ef/1c/ddcdbedb47affd72d2f4c5164fd9",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/0e/58/c7da77e4c0d031073e4db047e4f3", "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/4a/ed/e7b293c3d484257e9c4a6c4b5ce1",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/f5/be/f80777972954ebe6fd91b52a6533", "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/c1/ed/4691901b692a59e598bd982f6d5b",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/e4/20/0f1e3a2e343e48dbe3c3ae8c6ab7", "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/02/74/f6091bf03a2de2690ac9de36391a",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/23/46/72f453ea380bd5f04957886c2b57", "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/fa/8c/79e39ee8297c9946f3dfed5d47c4",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/f8/e3/b7bf2bdd4fe4879e8f95bc56073c", "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/f0/c4/0554dbc45078ce12e3ab1ab00667",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/b5/e6/451f3cc73b5b79d21b19c2416d61", "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/2a/c3/4378d0290b122a345787b46ef23c",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/b2/0e/778420dfd1a6f81ed457a94f8f1d", "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/f1/6a/dcbae2252ce071fd07a350615351",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/00/76/0f4dd3bbf7a98f00221307535b4f", "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/c2/4b/04f9d9a58fa1d1cefc096cf84e57",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/4d/fe/1d4e9c927e74f900766cb3d3c55f", "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/61/8c/ec3c377382be6503836613fe0ae0",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/82/99/e0a873f37e95674f2151ea99bf3d", "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/65/8f/25f37b8f6175fbe9274576e2eb4a",
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/b3/a2/5da0c4dc65f469e4a476e0395eb5", "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/50/4e/39e4ff62ab5c2c3e72862c2c0881",
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/a9/9a/adb83188f9c7d7b51dafd0f8b8a8", "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/9d/2b/44367bfb114e7b1148e4e100acd1",
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/b3/a2/5da0c4dc65f469e4a476e0395eb5", "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/50/4e/39e4ff62ab5c2c3e72862c2c0881",
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/a9/9a/adb83188f9c7d7b51dafd0f8b8a8", "build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/9d/2b/44367bfb114e7b1148e4e100acd1",
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/27/f4/269f5d37a8e3938c0acdab299833", "build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/eb/1a/21761c11e160525903190cfe4ed3",
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/7b/f3/f98278c9654a972baf65d5f04c12", "build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/a3/38/2c4abbc88434720311048185fdd1",
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/27/f4/269f5d37a8e3938c0acdab299833", "build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/eb/1a/21761c11e160525903190cfe4ed3",
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/7b/f3/f98278c9654a972baf65d5f04c12", "build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/a3/38/2c4abbc88434720311048185fdd1",
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/c7/a2/40728a3ebfb3006c7a47b698214f", "build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/5f/47/5e5c2e910c783b15c6dcf0d78715",
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/84/19/a1bbbf42c50329f0cd1377d103bb", "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/75/af/1ca3bec27e72101585254739189d",
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/c7/a2/40728a3ebfb3006c7a47b698214f", "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/5f/47/5e5c2e910c783b15c6dcf0d78715",
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/84/19/a1bbbf42c50329f0cd1377d103bb", "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/75/af/1ca3bec27e72101585254739189d",
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/53/e4/455c68ee50813fe89e3002cf1fe8", "build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/0b/6e/67ffc264f249cde0044637982bab",
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/59/fe/a3e369f2db87a305641e74ae70ab", "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/06/ab/699f575fabf3819dcf40b4c3125e",
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/be/df/60063a6845e8654958f1a3e37867", "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/b3/27/a8e1a856695454d8b37a7d4f3604",
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/59/fe/a3e369f2db87a305641e74ae70ab", "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/06/ab/699f575fabf3819dcf40b4c3125e",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/91/2f/362a643d543963de549d830fe604", "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/b0/37/db1f330cc6d7d6cf987ff55b9ed4",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/2e/d4/67d6c0f9b372eb5cd92c9def6fc8", "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/b0/55/536be0beef09a49ecca957ca7318",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/68/8b/d6049425f1069d256abdaf90004c", "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/d1/06/f341df353f2df7dbcbc80e2f3987",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/3e/82/f70e75696765ac05875cb5dd778c", "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/bd/f3/6f99da945c68fae7f860e69f86c6",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/38/ee/56658557aa2ecabd0d30eb01b68e", "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/ea/2d/ca8db3623be87b383486f905f41c",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/6b/4c/568766d02bbca174752488850737", "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/1e/fa/09a99039de3bf810e3ed13959416",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/3e/5b/1aa2252706188de69075eb7b3656", "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/cb/b0/f146f9d033d82a1d03496360001c",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/ab/a7/21bc5acc8a823bd7c7ec940e97bc", "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/e1/5d/453846d79912b5a9a82cb5f8e2f5",
"src/assets/ba_data/python/babase/_mgen/__init__.py": "https://files.ballistica.net/cache/ba1/f8/85/fed7f2ed98ff2ba271f9dbe3391c", "src/assets/ba_data/python/babase/_mgen/__init__.py": "https://files.ballistica.net/cache/ba1/f8/85/fed7f2ed98ff2ba271f9dbe3391c",
"src/assets/ba_data/python/babase/_mgen/enums.py": "https://files.ballistica.net/cache/ba1/f8/cd/3af311ac63147882590123b78318", "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/ee/dd/ad968b176000e31c65be6206a2bc", "src/ballistica/base/mgen/pyembed/binding_base.inc": "https://files.ballistica.net/cache/ba1/ee/dd/ad968b176000e31c65be6206a2bc",

View File

@ -125,6 +125,7 @@
<w>appletvsimulator</w> <w>appletvsimulator</w>
<w>appmode</w> <w>appmode</w>
<w>appmodeselector</w> <w>appmodeselector</w>
<w>appmodule</w>
<w>appname</w> <w>appname</w>
<w>appnameupper</w> <w>appnameupper</w>
<w>appnow</w> <w>appnow</w>
@ -904,6 +905,7 @@
<w>enumvalue</w> <w>enumvalue</w>
<w>enval</w> <w>enval</w>
<w>envcfg</w> <w>envcfg</w>
<w>envconfig</w>
<w>envglobals</w> <w>envglobals</w>
<w>envhash</w> <w>envhash</w>
<w>envname</w> <w>envname</w>
@ -1892,6 +1894,7 @@
<w>namedarg</w> <w>namedarg</w>
<w>namel</w> <w>namel</w>
<w>namepre</w> <w>namepre</w>
<w>namepretty</w>
<w>nametext</w> <w>nametext</w>
<w>nameu</w> <w>nameu</w>
<w>nameval</w> <w>nameval</w>
@ -2432,6 +2435,7 @@
<w>redist</w> <w>redist</w>
<w>redistributables</w> <w>redistributables</w>
<w>reenabled</w> <w>reenabled</w>
<w>reexpose</w>
<w>regionid</w> <w>regionid</w>
<w>registerexecutionpolicyexception</w> <w>registerexecutionpolicyexception</w>
<w>registerwithlaunchservices</w> <w>registerwithlaunchservices</w>

View File

@ -113,6 +113,8 @@
<option value="typing_extensions.assert_type" /> <option value="typing_extensions.assert_type" />
<option value="float.__getitem__" /> <option value="float.__getitem__" />
<option value="pbxproj.pbxsections.PBXGroup.PBXGroup.children" /> <option value="pbxproj.pbxsections.PBXGroup.PBXGroup.children" />
<option value="bascenev1lib.game.race.Player.__getitem__" />
<option value="batools.project._updater._LineChange.__getitem__" />
</list> </list>
</option> </option>
</inspection_tool> </inspection_tool>

View File

@ -1,5 +1,11 @@
### 1.7.24 (build 21196, api 8, 2023-07-25) ### 1.7.24 (build 21199, api 8, 2023-07-27)
- Fixed an issue where respawn icons could disappear in epic mode (Thanks for
the heads-up Rikko!)
- The `BA_ENABLE_IRONY_BUILD_DB` optional build env-var is now
`BA_ENABLE_COMPILE_COMMANDS_DB` since this same functionality can be used by
clangd or other tools. Originally I was using it for Irony for Emacs; hence
the old name.
- Due to the cleanup done in 1.7.20, it is now possible to build and run - Due to the cleanup done in 1.7.20, it is now possible to build and run
Ballistica as a 'pure' Python app consisting of binary Python modules loaded Ballistica as a 'pure' Python app consisting of binary Python modules loaded
by a standard Python interpreter. This new build style is referred to as by a standard Python interpreter. This new build style is referred to as

View File

@ -28,11 +28,11 @@
help: help:
@tools/pcommand makefile_target_list Makefile @tools/pcommand makefile_target_list Makefile
# Set env-var BA_ENABLE_IRONY_BUILD_DB=1 to enable creating/updating a cmake # Set env-var BA_ENABLE_COMPILE_COMMANDS_DB=1 to enable creating/updating a
# compile-commands database for use with irony for emacs (and possibly other # cmake compile-commands database for use with irony for emacs (and possibly
# tools). # other tools).
ifeq ($(BA_ENABLE_IRONY_BUILD_DB),1) ifeq ($(BA_ENABLE_COMPILE_COMMANDS_DB),1)
PREREQ_IRONY_BUILD_DB = .cache/irony/compile_commands.json PREREQ_COMPILE_COMMANDS_DB = .cache/compile_commands_db/compile_commands.json
endif endif
# Prereq targets that should be safe to run anytime; even if project-files # Prereq targets that should be safe to run anytime; even if project-files
@ -45,7 +45,7 @@ PREREQS_SAFE = .cache/checkenv .dir-locals.el .mypy.ini .pycheckers .pylintrc \
# fail if the CMakeList files don't match what's on disk. If such a target was # fail if the CMakeList files don't match what's on disk. If such a target was
# included in PREREQS_SAFE it would try to build *before* project updates # included in PREREQS_SAFE it would try to build *before* project updates
# which would leave us stuck in a broken state. # which would leave us stuck in a broken state.
PREREQS_POST_UPDATE_ONLY = $(PREREQ_IRONY_BUILD_DB) PREREQS_POST_UPDATE_ONLY = $(PREREQ_COMPILE_COMMANDS_DB)
# Target that should be built before running most any other build. # Target that should be built before running most any other build.
# This installs tool config files, runs environment checks, etc. # This installs tool config files, runs environment checks, etc.
@ -1010,20 +1010,20 @@ CMAKE_BUILD_TYPE ?= Debug
# Build and run the cmake build. # Build and run the cmake build.
cmake: cmake-build cmake: cmake-build
@cd build/cmake/$(CM_BT_LC)/staged && ./ballisticakit cd build/cmake/$(CM_BT_LC)/staged && ./ballisticakit
# Build and run the cmake build under the gdb debugger. # Build and run the cmake build under the gdb debugger.
# Sets up the ballistica environment to do things like abort() out to the # Sets up the ballistica environment to do things like abort() out to the
# debugger on errors instead of trying to cleanly exit. # debugger on errors instead of trying to cleanly exit.
cmake-gdb: cmake-build cmake-gdb: cmake-build
@cd build/cmake/$(CM_BT_LC)/staged && \ cd build/cmake/$(CM_BT_LC)/staged && \
BA_DEBUGGER_ATTACHED=1 gdb ./ballisticakit BA_DEBUGGER_ATTACHED=1 gdb ./ballisticakit
# Build and run the cmake build under the lldb debugger. # Build and run the cmake build under the lldb debugger.
# Sets up the ballistica environment to do things like abort() out to the # Sets up the ballistica environment to do things like abort() out to the
# debugger on errors instead of trying to cleanly exit. # debugger on errors instead of trying to cleanly exit.
cmake-lldb: cmake-build cmake-lldb: cmake-build
@cd build/cmake/$(CM_BT_LC)/staged && \ cd build/cmake/$(CM_BT_LC)/staged && \
BA_DEBUGGER_ATTACHED=1 lldb ./ballisticakit BA_DEBUGGER_ATTACHED=1 lldb ./ballisticakit
# Build but don't run it. # Build but don't run it.
@ -1045,7 +1045,7 @@ cmake-clean:
rm -rf build/cmake/$(CM_BT_LC) rm -rf build/cmake/$(CM_BT_LC)
cmake-server: cmake-server-build cmake-server: cmake-server-build
@cd build/cmake/server-$(CM_BT_LC)/staged && ./ballisticakit_server cd build/cmake/server-$(CM_BT_LC)/staged && ./ballisticakit_server
cmake-server-build: assets-server meta cmake-server-binary cmake-server-build: assets-server meta cmake-server-binary
@$(STAGE_BUILD) -cmakeserver -$(CM_BT_LC) \ @$(STAGE_BUILD) -cmakeserver -$(CM_BT_LC) \
@ -1074,7 +1074,7 @@ cmake-modular-build: assets-cmake meta cmake-modular-binary
Modular build complete: BLU build/cmake/modular-$(CM_BT_LC)/staged Modular build complete: BLU build/cmake/modular-$(CM_BT_LC)/staged
cmake-modular: cmake-modular-build cmake-modular: cmake-modular-build
@cd build/cmake/modular-$(CM_BT_LC)/staged && ./ballisticakit cd build/cmake/modular-$(CM_BT_LC)/staged && ./ballisticakit
cmake-modular-binary: meta cmake-modular-binary: meta
@tools/pcommand cmake_prep_dir build/cmake/modular-$(CM_BT_LC) @tools/pcommand cmake_prep_dir build/cmake/modular-$(CM_BT_LC)
@ -1089,7 +1089,7 @@ cmake-modular-clean:
rm -rf build/cmake/modular-$(CM_BT_LC) rm -rf build/cmake/modular-$(CM_BT_LC)
cmake-modular-server: cmake-modular-server-build cmake-modular-server: cmake-modular-server-build
@cd build/cmake/modular-server-$(CM_BT_LC)/staged && ./ballisticakit_server cd build/cmake/modular-server-$(CM_BT_LC)/staged && ./ballisticakit_server
cmake-modular-server-build: assets-server meta cmake-modular-server-binary cmake-modular-server-build: assets-server meta cmake-modular-server-binary
@$(STAGE_BUILD) -cmakemodularserver -$(CM_BT_LC) \ @$(STAGE_BUILD) -cmakemodularserver -$(CM_BT_LC) \
@ -1221,21 +1221,21 @@ ballisticakit-cmake/.clang-format: .clang-format
@mkdir -p ballisticakit-cmake @mkdir -p ballisticakit-cmake
@cd ballisticakit-cmake && ln -sf ../.clang-format . @cd ballisticakit-cmake && ln -sf ../.clang-format .
# Irony in emacs requires us to use cmake to generate a full # Various tools such as Irony for Emacs or clangd make use of a list of
# list of compile commands for all files; lets try to keep it up to date # compile commands for all files; lets try to keep it up to date
# whenever CMakeLists changes. # whenever CMakeLists changes.
.cache/irony/compile_commands.json: ballisticakit-cmake/CMakeLists.txt .cache/compile_commands_db/compile_commands.json: \
@tools/pcommand echo BLU Updating Irony build commands db... ballisticakit-cmake/CMakeLists.txt
@echo Generating Irony compile-commands-list... @tools/pcommand echo BLU Updating compile commands db...
@mkdir -p .cache/irony @mkdir -p .cache/compile_commands_db
@cd .cache/irony \ @cd .cache/compile_commands_db \
&& cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug \ && cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug \
$(shell pwd)/ballisticakit-cmake $(shell pwd)/ballisticakit-cmake
@mv .cache/irony/compile_commands.json . \ @mv .cache/compile_commands_db/compile_commands.json . \
&& rm -rf .cache/irony \ && rm -rf .cache/compile_commands_db \
&& mkdir .cache/irony \ && mkdir .cache/compile_commands_db \
&& mv compile_commands.json .cache/irony && mv compile_commands.json .cache/compile_commands_db
@tools/pcommand echo BLU Created Irony build db at $@ @tools/pcommand echo BLU Created compile commands db at $@
_windows-wsl-build: _windows-wsl-build:
@tools/pcommand wsl_build_check_win_drive @tools/pcommand wsl_build_check_win_drive

View File

@ -83,6 +83,7 @@
<w>appletvsimulator</w> <w>appletvsimulator</w>
<w>appmode</w> <w>appmode</w>
<w>appmodeselector</w> <w>appmodeselector</w>
<w>appmodule</w>
<w>appname</w> <w>appname</w>
<w>appnameupper</w> <w>appnameupper</w>
<w>appnow</w> <w>appnow</w>
@ -549,6 +550,7 @@
<w>enumvalue</w> <w>enumvalue</w>
<w>enval</w> <w>enval</w>
<w>envcfg</w> <w>envcfg</w>
<w>envconfig</w>
<w>envglobals</w> <w>envglobals</w>
<w>envs</w> <w>envs</w>
<w>envval</w> <w>envval</w>
@ -1102,6 +1104,7 @@
<w>namecap</w> <w>namecap</w>
<w>namel</w> <w>namel</w>
<w>namepre</w> <w>namepre</w>
<w>namepretty</w>
<w>nameu</w> <w>nameu</w>
<w>nameval</w> <w>nameval</w>
<w>nbuffer</w> <w>nbuffer</w>
@ -1430,6 +1433,7 @@
<w>recvfrom</w> <w>recvfrom</w>
<w>redundants</w> <w>redundants</w>
<w>reenabled</w> <w>reenabled</w>
<w>reexpose</w>
<w>refcounted</w> <w>refcounted</w>
<w>refl</w> <w>refl</w>
<w>regionid</w> <w>regionid</w>

View File

@ -2,7 +2,8 @@
;;; For more information see (info "(emacs) Directory Variables") ;;; For more information see (info "(emacs) Directory Variables")
;;; Turn flycheck mode on for our c++ stuff and tell jedi where to look for our python stuff. ;;; Turn flycheck mode on for our c++ stuff and tell jedi where to look for our python stuff.
((c++-mode (eval . (flycheck-mode))) (
;; (c++-mode (eval . (flycheck-mode)))
(python-ts-mode (jedi:server-args . ("--sys-path" "__EFRO_PROJECT_ROOT__/tools" (python-ts-mode (jedi:server-args . ("--sys-path" "__EFRO_PROJECT_ROOT__/tools"
@ -22,8 +23,18 @@
(nil . ((projectile-globally-ignored-directories . ("docs" (nil . ((projectile-globally-ignored-directories . ("docs"
"submodules" "submodules"
"src/external" "src/external"
"src/assets/ba_data/python-site-packages"
"src/assets/pylib-android" "src/assets/pylib-android"
"src/assets/pylib-apple" "src/assets/pylib-apple"
"src/assets/windows")))) "src/assets/windows"))))
;; Trying to get project.el to work the same as projectile since its built in.
(nil . ((project-vc-ignores . ("docs"
"submodules"
"src/external"
"src/assets/ba_data/python-site-packages"
"src/assets/pylib-android"
"src/assets/pylib-apple"
"src/assets/windows"))))
) )

View File

@ -41,6 +41,7 @@ from _babase import (
fade_screen, fade_screen,
fatal_error, fatal_error,
get_display_resolution, get_display_resolution,
get_immediate_return_code,
get_low_level_config_value, get_low_level_config_value,
get_max_graphics_quality, get_max_graphics_quality,
get_replays_dir, get_replays_dir,
@ -204,6 +205,7 @@ __all__ = [
'fatal_error', 'fatal_error',
'garbage_collect', 'garbage_collect',
'get_display_resolution', 'get_display_resolution',
'get_immediate_return_code',
'get_ip_address_type', 'get_ip_address_type',
'get_low_level_config_value', 'get_low_level_config_value',
'get_max_graphics_quality', 'get_max_graphics_quality',

View File

@ -156,11 +156,11 @@ def on_app_launching() -> None:
assert _babase.in_logic_thread() assert _babase.in_logic_thread()
# Let the user know if the app Python dir is a custom one. # Let the user know if the app Python dir is a 'user' one.
user_sys_scripts_dir = baenv.get_user_system_scripts_dir() envconfig = baenv.get_config()
if user_sys_scripts_dir is not None: if envconfig.is_user_app_python_dir:
_babase.screenmessage( _babase.screenmessage(
f"Using user system scripts: '{user_sys_scripts_dir}'", f"Using user system scripts: '{envconfig.app_python_dir}'",
color=(0.6, 0.6, 1.0), color=(0.6, 0.6, 1.0),
) )

View File

@ -24,20 +24,19 @@ from dataclasses import dataclass
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import __main__ import __main__
from efro.log import setup_logging, LogLevel
if TYPE_CHECKING: if TYPE_CHECKING:
from efro.log import LogHandler from efro.log import LogHandler
# IMPORTANT - It is likely (and in some cases expected) that this # IMPORTANT - It is likely (and in some cases expected) that this
# module's code will be exec'ed multiple times. This is because it is # module's code will be exec'ed multiple times. This is because it is
# the job of this module to set up paths for an engine run, and that may # the job of this module to set up Python paths for an engine run, and
# involve modifying sys.path in such a way that this module resolves to # that may involve modifying sys.path in such a way that this module
# a different path afterwards (for example from # resolves to a different path afterwards (for example from
# /abs/path/to/ba_data/scripts/babase.py to ba_data/scripts/babase.py). # /abs/path/to/ba_data/scripts/babase.py to ba_data/scripts/babase.py).
# This can result in the next import of baenv loading us from our 'new' # This can result in the next import of baenv loading us from our 'new'
# location, which may or may not actually be the same file on disk as # location, which may or may not actually be the same file on disk as
# the old. Either way, however, multiple execs will happen in some form. # the last load. Either way, however, multiple execs will happen in some
# form.
# #
# So we need to do a few things to handle that situation gracefully. # So we need to do a few things to handle that situation gracefully.
# #
@ -51,13 +50,13 @@ if TYPE_CHECKING:
# Build number and version of the ballistica binary we expect to be # Build number and version of the ballistica binary we expect to be
# using. # using.
TARGET_BALLISTICA_BUILD = 21196 TARGET_BALLISTICA_BUILD = 21199
TARGET_BALLISTICA_VERSION = '1.7.24' TARGET_BALLISTICA_VERSION = '1.7.24'
@dataclass @dataclass
class EnvConfig: class EnvConfig:
"""Environment put together by the configure call.""" """Environment values put together by the configure call."""
config_dir: str config_dir: str
data_dir: str data_dir: str
@ -66,49 +65,45 @@ class EnvConfig:
standard_app_python_dir: str standard_app_python_dir: str
site_python_dir: str | None site_python_dir: str | None
log_handler: LogHandler | None log_handler: LogHandler | None
is_user_app_python_dir: bool
@dataclass @dataclass
class EnvGlobals: class _EnvGlobals:
"""Our globals we store in the main module.""" """Our globals we store in the main module."""
config: EnvConfig | None = None config: EnvConfig | None = None
config_called: bool = False called_configure: bool = False
paths_set_failed: bool = False paths_set_failed: bool = False
user_system_scripts_dir: str | None = None modular_main_called: bool = False
@classmethod @classmethod
def get(cls) -> EnvGlobals: def get(cls) -> _EnvGlobals:
"""Create/return our singleton.""" """Create/return our singleton."""
name = '_baenv_globals' name = '_baenv_globals'
envglobals: EnvGlobals | None = getattr(__main__, name, None) envglobals: _EnvGlobals | None = getattr(__main__, name, None)
if envglobals is None: if envglobals is None:
envglobals = EnvGlobals() envglobals = _EnvGlobals()
setattr(__main__, name, envglobals) setattr(__main__, name, envglobals)
return envglobals return envglobals
def did_paths_set_fail() -> bool:
"""Did we try to set paths and failed?"""
return _EnvGlobals.get().paths_set_failed
def config_exists() -> bool: def config_exists() -> bool:
"""Has a config been created?""" """Has a config been created?"""
return EnvGlobals.get().config is not None return _EnvGlobals.get().config is not None
def did_paths_set_fail() -> bool:
"""Did we try to set paths and failed?"""
return EnvGlobals.get().paths_set_failed
def get_user_system_scripts_dir() -> str | None:
"""If there's a custom user system scripts dir in play, return it."""
return EnvGlobals.get().user_system_scripts_dir
def get_config() -> EnvConfig: def get_config() -> EnvConfig:
"""Return the active config, creating a default if none exists.""" """Return the active config, creating a default if none exists."""
envglobals = EnvGlobals.get() envglobals = _EnvGlobals.get()
if not envglobals.config_called: if not envglobals.called_configure:
configure() configure()
config = envglobals.config config = envglobals.config
@ -128,7 +123,7 @@ def configure(
site_python_dir: str | None = None, site_python_dir: str | None = None,
contains_python_dist: bool = False, contains_python_dist: bool = False,
) -> None: ) -> None:
"""Set up the Python environment for running a ballistica app. """Set up the Python environment for running a Ballistica app.
This includes things such as Python path wrangling and app directory This includes things such as Python path wrangling and app directory
creation. This should be called before any other ballistica modules creation. This should be called before any other ballistica modules
@ -136,14 +131,14 @@ def configure(
where those modules get loaded from. where those modules get loaded from.
""" """
envglobals = EnvGlobals.get() envglobals = _EnvGlobals.get()
if envglobals.config_called: if envglobals.called_configure:
raise RuntimeError( raise RuntimeError(
'baenv.configure() has already been called;' 'baenv.configure() has already been called;'
' it can only be called once.' ' it can only be called once.'
) )
envglobals.config_called = True envglobals.called_configure = True
# The very first thing we do is set up our logging system and pipe # The very first thing we do is set up our logging system and pipe
# Python's stdout/stderr into it. Then we can at least debug # Python's stdout/stderr into it. Then we can at least debug
@ -167,6 +162,7 @@ def configure(
data_dir, data_dir,
config_dir, config_dir,
standard_app_python_dir, standard_app_python_dir,
is_user_app_python_dir,
) = _setup_paths( ) = _setup_paths(
user_python_dir, user_python_dir,
app_python_dir, app_python_dir,
@ -190,6 +186,7 @@ def configure(
standard_app_python_dir=standard_app_python_dir, standard_app_python_dir=standard_app_python_dir,
site_python_dir=site_python_dir, site_python_dir=site_python_dir,
log_handler=log_handler, log_handler=log_handler,
is_user_app_python_dir=is_user_app_python_dir,
) )
@ -215,6 +212,8 @@ def _calc_data_dir(data_dir: str | None) -> str:
def _setup_logging() -> LogHandler: def _setup_logging() -> LogHandler:
from efro.log import setup_logging, LogLevel
log_handler = setup_logging( log_handler = setup_logging(
log_path=None, log_path=None,
level=LogLevel.DEBUG, level=LogLevel.DEBUG,
@ -249,11 +248,11 @@ def _setup_paths(
site_python_dir: str | None, site_python_dir: str | None,
data_dir: str | None, data_dir: str | None,
config_dir: str | None, config_dir: str | None,
) -> tuple[str | None, str | None, str | None, str, str, str]: ) -> tuple[str | None, str | None, str | None, str, str, str, bool]:
# First a few paths we can ALWAYS calculate since they don't affect # First a few paths we can ALWAYS calculate since they don't affect
# Python imports: # Python imports:
envglobals = EnvGlobals.get() envglobals = _EnvGlobals.get()
data_dir = _calc_data_dir(data_dir) data_dir = _calc_data_dir(data_dir)
@ -264,6 +263,9 @@ def _setup_paths(
# Standard app-python-dir is simply ba_data/python under data-dir. # Standard app-python-dir is simply ba_data/python under data-dir.
standard_app_python_dir = str(Path(data_dir, 'ba_data', 'python')) standard_app_python_dir = str(Path(data_dir, 'ba_data', 'python'))
# Whether the final app-dir we're returning is a custom user-owned one.
is_user_app_python_dir = False
# If _babase has already been imported, there's not much we can do # If _babase has already been imported, there's not much we can do
# at this point aside from complain and inform for next time. # at this point aside from complain and inform for next time.
if '_babase' in sys.modules: if '_babase' in sys.modules:
@ -299,7 +301,8 @@ def _setup_paths(
# stuff. # stuff.
check_dir = Path(user_python_dir, 'sys', TARGET_BALLISTICA_VERSION) check_dir = Path(user_python_dir, 'sys', TARGET_BALLISTICA_VERSION)
if check_dir.is_dir(): if check_dir.is_dir():
envglobals.user_system_scripts_dir = app_python_dir = str(check_dir) app_python_dir = str(check_dir)
is_user_app_python_dir = True
# Ok, now apply these to sys.path. # Ok, now apply these to sys.path.
@ -342,6 +345,7 @@ def _setup_paths(
data_dir, data_dir,
config_dir, config_dir,
standard_app_python_dir, standard_app_python_dir,
is_user_app_python_dir,
) )
@ -361,16 +365,96 @@ def _setup_dirs(config_dir: str | None, user_python_dir: str | None) -> None:
) )
def _main() -> None: def extract_arg(args: list[str], names: list[str], is_dir: bool) -> str | None:
# Run a default configure BEFORE importing babase. """Given a list of args and an arg name, returns a value.
# (may affect where babase comes from).
configure()
import babase The arg flag and value are removed from the arg list. We also check
to make sure the path exists.
babase.app.run() raises CleanErrors on any problems.
"""
from efro.error import CleanError
count = sum(args.count(n) for n in names)
if not count:
return None
if count > 1:
raise CleanError(f'Arg {names} passed multiple times.')
for name in names:
if name not in args:
continue
argindex = args.index(name)
if argindex + 1 >= len(args):
raise CleanError(f'No value passed after {name} arg.')
val = args[argindex + 1]
del args[argindex : argindex + 2]
if is_dir and not os.path.isdir(val):
namepretty = names[0].removeprefix('--')
raise CleanError(
f"Provided {namepretty} path '{val}' is not a directory."
)
return val
raise RuntimeError(f'Expected arg name not found from {names}')
# Allow exec'ing this module directly to do a standard app run. def _modular_main() -> None:
from efro.error import CleanError
try:
# Take note that we're running via modular-main.
# The native layer can key off this to know whether it
# should apply sys.argv or not.
_EnvGlobals.get().modular_main_called = True
# We run configure() BEFORE importing babase. (part of its job
# is to set up where babase and everything else gets loaded
# from).
# We deal with a few key ours here ourself before spinning up
# any engine stuff.
# NOTE: Need to keep these arg long/short versions synced to
# those in core_config.cc since they parse the same values (they
# just don't handle them).
args = sys.argv.copy()
# Our -c arg basically mirrors Python's -c arg. If we get that,
# simply exec it and return; no engine stuff.
command = extract_arg(args, ['--command', '-c'], is_dir=False)
if command is not None:
exec(command) # pylint: disable=exec-used
return
config_dir = extract_arg(args, ['--config-dir', '-C'], is_dir=True)
data_dir = extract_arg(args, ['--data-dir', '-d'], is_dir=True)
mods_dir = extract_arg(args, ['--mods-dir', '-m'], is_dir=True)
configure(
config_dir=config_dir,
data_dir=data_dir,
user_python_dir=mods_dir,
)
import babase
# The engine will have parsed and processed all other args as
# part of the above import. If there were errors or args such as
# --help which should lead to immediate returns, do so.
code = babase.get_immediate_return_code()
if code is not None:
sys.exit(code)
babase.app.run()
except CleanError as clean_exc:
clean_exc.pretty_print()
sys.exit(1)
# Exec'ing this module directly will do a standard app run.
if __name__ == '__main__': if __name__ == '__main__':
_main() _modular_main()

View File

@ -45,6 +45,7 @@ from babase import (
NodeNotFoundError, NodeNotFoundError,
normalized_color, normalized_color,
NotFoundError, NotFoundError,
PlayerNotFoundError,
Plugin, Plugin,
pushcall, pushcall,
safecolor, safecolor,
@ -374,6 +375,7 @@ __all__ = [
'PlayerDiedMessage', 'PlayerDiedMessage',
'PlayerProfilesChangedMessage', 'PlayerProfilesChangedMessage',
'PlayerInfo', 'PlayerInfo',
'PlayerNotFoundError',
'PlayerRecord', 'PlayerRecord',
'PlayerScoredMessage', 'PlayerScoredMessage',
'Plugin', 'Plugin',

View File

@ -4,14 +4,8 @@
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING
import babase
import bascenev1 as bs import bascenev1 as bs
if TYPE_CHECKING:
pass
class CoopJoinActivity(bs.JoinActivity): class CoopJoinActivity(bs.JoinActivity):
"""Join-screen for co-op mode.""" """Join-screen for co-op mode."""
@ -46,7 +40,7 @@ class CoopJoinActivity(bs.JoinActivity):
).autoretain() ).autoretain()
ControlsGuide(delay=1.0).autoretain() ControlsGuide(delay=1.0).autoretain()
babase.pushcall(self._show_remaining_achievements) bs.pushcall(self._show_remaining_achievements)
def _show_remaining_achievements(self) -> None: def _show_remaining_achievements(self) -> None:
from bascenev1lib.actor.text import Text from bascenev1lib.actor.text import Text
@ -70,22 +64,22 @@ class CoopJoinActivity(bs.JoinActivity):
ts_h_offs = 60 ts_h_offs = 60
# Show remaining achievements in some cases. # Show remaining achievements in some cases.
if babase.app.classic is not None and not ( if bs.app.classic is not None and not (
babase.app.demo_mode or babase.app.arcade_mode bs.app.demo_mode or bs.app.arcade_mode
): ):
achievements = [ achievements = [
a a
for a in babase.app.classic.ach.achievements_for_coop_level( for a in bs.app.classic.ach.achievements_for_coop_level(
levelname levelname
) )
if not a.complete if not a.complete
] ]
have_achievements = bool(achievements) have_achievements = bool(achievements)
achievements = [a for a in achievements if not a.complete] achievements = [a for a in achievements if not a.complete]
vrmode = babase.app.vr_mode vrmode = bs.app.vr_mode
if have_achievements: if have_achievements:
Text( Text(
babase.Lstr(resource='achievementsRemainingText'), bs.Lstr(resource='achievementsRemainingText'),
host_only=True, host_only=True,
position=(ts_h_offs - 10, vpos), position=(ts_h_offs - 10, vpos),
transition=Text.Transition.FADE_IN, transition=Text.Transition.FADE_IN,
@ -105,7 +99,7 @@ class CoopJoinActivity(bs.JoinActivity):
vpos -= 55 vpos -= 55
if not achievements: if not achievements:
Text( Text(
babase.Lstr(resource='noAchievementsRemainingText'), bs.Lstr(resource='noAchievementsRemainingText'),
host_only=True, host_only=True,
position=(ts_h_offs + 15, vpos + 10), position=(ts_h_offs + 15, vpos + 10),
transition=Text.Transition.FADE_IN, transition=Text.Transition.FADE_IN,

View File

@ -4,15 +4,9 @@
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING import bascenev1 as bs
import babase
from bascenev1lib.activity.multiteamscore import MultiTeamScoreScreenActivity from bascenev1lib.activity.multiteamscore import MultiTeamScoreScreenActivity
from bascenev1lib.actor.zoomtext import ZoomText from bascenev1lib.actor.zoomtext import ZoomText
import bascenev1 as bs
if TYPE_CHECKING:
pass
class DrawScoreScreenActivity(MultiTeamScoreScreenActivity): class DrawScoreScreenActivity(MultiTeamScoreScreenActivity):
@ -21,10 +15,10 @@ class DrawScoreScreenActivity(MultiTeamScoreScreenActivity):
default_music = None # Awkward silence... default_music = None # Awkward silence...
def on_begin(self) -> None: def on_begin(self) -> None:
babase.set_analytics_screen('Draw Score Screen') bs.set_analytics_screen('Draw Score Screen')
super().on_begin() super().on_begin()
ZoomText( ZoomText(
babase.Lstr(resource='drawText'), bs.Lstr(resource='drawText'),
position=(0, 0), position=(0, 0),
maxwidth=400, maxwidth=400,
shiftposition=(-220, 0), shiftposition=(-220, 0),

View File

@ -4,16 +4,10 @@
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING
import babase
import bascenev1 as bs import bascenev1 as bs
from bascenev1lib.activity.multiteamscore import MultiTeamScoreScreenActivity from bascenev1lib.activity.multiteamscore import MultiTeamScoreScreenActivity
from bascenev1lib.actor.zoomtext import ZoomText from bascenev1lib.actor.zoomtext import ZoomText
if TYPE_CHECKING:
pass
class TeamVictoryScoreScreenActivity(MultiTeamScoreScreenActivity): class TeamVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
"""Scorescreen between rounds of a dual-team session.""" """Scorescreen between rounds of a dual-team session."""
@ -24,7 +18,7 @@ class TeamVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
assert isinstance(self._winner, bs.SessionTeam) assert isinstance(self._winner, bs.SessionTeam)
def on_begin(self) -> None: def on_begin(self) -> None:
babase.set_analytics_screen('Teams Score Screen') bs.set_analytics_screen('Teams Score Screen')
super().on_begin() super().on_begin()
height = 130 height = 130
@ -37,13 +31,13 @@ class TeamVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
# 'First to 4'. # 'First to 4'.
session = self.session session = self.session
assert isinstance(session, bs.MultiTeamSession) assert isinstance(session, bs.MultiTeamSession)
if babase.app.lang.get_resource('bestOfUseFirstToInstead'): if bs.app.lang.get_resource('bestOfUseFirstToInstead'):
best_txt = babase.Lstr( best_txt = bs.Lstr(
resource='firstToSeriesText', resource='firstToSeriesText',
subs=[('${COUNT}', str(session.get_series_length() / 2 + 1))], subs=[('${COUNT}', str(session.get_series_length() / 2 + 1))],
) )
else: else:
best_txt = babase.Lstr( best_txt = bs.Lstr(
resource='bestOfSeriesText', resource='bestOfSeriesText',
subs=[('${COUNT}', str(session.get_series_length()))], subs=[('${COUNT}', str(session.get_series_length()))],
) )
@ -63,7 +57,7 @@ class TeamVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
for team in self.session.sessionteams: for team in self.session.sessionteams:
bs.timer( bs.timer(
i * 0.15 + 0.15, i * 0.15 + 0.15,
babase.WeakCall( bs.WeakCall(
self._show_team_name, self._show_team_name,
vval - i * height, vval - i * height,
team, team,
@ -78,7 +72,7 @@ class TeamVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
delay = 1.2 delay = 1.2
bs.timer( bs.timer(
i * 0.150 + 0.2, i * 0.150 + 0.2,
babase.WeakCall( bs.WeakCall(
self._show_team_old_score, self._show_team_old_score,
vval - i * height, vval - i * height,
team, team,
@ -89,7 +83,7 @@ class TeamVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
bs.timer( bs.timer(
i * 0.150 + delay, i * 0.150 + delay,
babase.WeakCall( bs.WeakCall(
self._show_team_score, self._show_team_score,
vval - i * height, vval - i * height,
team, team,
@ -110,7 +104,7 @@ class TeamVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
) -> None: ) -> None:
del kill_delay # Unused arg. del kill_delay # Unused arg.
ZoomText( ZoomText(
babase.Lstr(value='${A}:', subs=[('${A}', team.name)]), bs.Lstr(value='${A}:', subs=[('${A}', team.name)]),
position=(100, pos_v), position=(100, pos_v),
shiftposition=(-150, pos_v), shiftposition=(-150, pos_v),
shiftdelay=shiftdelay, shiftdelay=shiftdelay,

View File

@ -4,15 +4,9 @@
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING
import babase
import bascenev1 as bs import bascenev1 as bs
from bascenev1lib.actor.text import Text from bascenev1lib.actor.text import Text
if TYPE_CHECKING:
pass
class MultiTeamJoinActivity(bs.JoinActivity): class MultiTeamJoinActivity(bs.JoinActivity):
"""Join screen for teams sessions.""" """Join screen for teams sessions."""
@ -32,10 +26,10 @@ class MultiTeamJoinActivity(bs.JoinActivity):
# Show info about the next up game. # Show info about the next up game.
self._next_up_text = Text( self._next_up_text = Text(
babase.Lstr( bs.Lstr(
value='${1} ${2}', value='${1} ${2}',
subs=[ subs=[
('${1}', babase.Lstr(resource='upFirstText')), ('${1}', bs.Lstr(resource='upFirstText')),
('${2}', session.get_next_game_description()), ('${2}', session.get_next_game_description()),
], ],
), ),
@ -72,12 +66,12 @@ class MultiTeamJoinActivity(bs.JoinActivity):
).autoretain() ).autoretain()
Text( Text(
babase.Lstr( bs.Lstr(
resource='mustInviteFriendsText', resource='mustInviteFriendsText',
subs=[ subs=[
( (
'${GATHER}', '${GATHER}',
babase.Lstr(resource='gatherWindow.titleText'), bs.Lstr(resource='gatherWindow.titleText'),
) )
], ],
), ),

View File

@ -3,16 +3,10 @@
"""Functionality related to teams mode score screen.""" """Functionality related to teams mode score screen."""
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING
import babase
import bascenev1 as bs import bascenev1 as bs
from bascenev1lib.actor.text import Text from bascenev1lib.actor.text import Text
from bascenev1lib.actor.image import Image from bascenev1lib.actor.image import Image
if TYPE_CHECKING:
pass
class MultiTeamScoreScreenActivity(bs.ScoreScreenActivity): class MultiTeamScoreScreenActivity(bs.ScoreScreenActivity):
"""Base class for score screens.""" """Base class for score screens."""
@ -28,12 +22,12 @@ class MultiTeamScoreScreenActivity(bs.ScoreScreenActivity):
super().on_begin() super().on_begin()
session = self.session session = self.session
if self._show_up_next and isinstance(session, bs.MultiTeamSession): if self._show_up_next and isinstance(session, bs.MultiTeamSession):
txt = babase.Lstr( txt = bs.Lstr(
value='${A} ${B}', value='${A} ${B}',
subs=[ subs=[
( (
'${A}', '${A}',
babase.Lstr( bs.Lstr(
resource='upNextText', resource='upNextText',
subs=[ subs=[
('${COUNT}', str(session.get_game_number() + 1)) ('${COUNT}', str(session.get_game_number() + 1))
@ -84,7 +78,7 @@ class MultiTeamScoreScreenActivity(bs.ScoreScreenActivity):
return val return val
return p_rec.accumscore return p_rec.accumscore
def _get_prec_score_str(p_rec: bs.PlayerRecord) -> str | babase.Lstr: def _get_prec_score_str(p_rec: bs.PlayerRecord) -> str | bs.Lstr:
if is_free_for_all and results is not None: if is_free_for_all and results is not None:
assert isinstance(results, bs.GameResults) assert isinstance(results, bs.GameResults)
assert p_rec.team.activityteam is not None assert p_rec.team.activityteam is not None
@ -135,7 +129,7 @@ class MultiTeamScoreScreenActivity(bs.ScoreScreenActivity):
def _txt( def _txt(
xoffs: float, xoffs: float,
yoffs: float, yoffs: float,
text: babase.Lstr, text: bs.Lstr,
h_align: Text.HAlign = Text.HAlign.RIGHT, h_align: Text.HAlign = Text.HAlign.RIGHT,
extrascale: float = 1.0, extrascale: float = 1.0,
maxwidth: float | None = 120.0, maxwidth: float | None = 120.0,
@ -157,7 +151,7 @@ class MultiTeamScoreScreenActivity(bs.ScoreScreenActivity):
session = self.session session = self.session
assert isinstance(session, bs.MultiTeamSession) assert isinstance(session, bs.MultiTeamSession)
tval = babase.Lstr( tval = bs.Lstr(
resource='gameLeadersText', resource='gameLeadersText',
subs=[('${COUNT}', str(session.get_game_number()))], subs=[('${COUNT}', str(session.get_game_number()))],
) )
@ -169,14 +163,12 @@ class MultiTeamScoreScreenActivity(bs.ScoreScreenActivity):
extrascale=1.4, extrascale=1.4,
maxwidth=None, maxwidth=None,
) )
_txt( _txt(-15, 4, bs.Lstr(resource='playerText'), h_align=Text.HAlign.LEFT)
-15, 4, babase.Lstr(resource='playerText'), h_align=Text.HAlign.LEFT _txt(180, 4, bs.Lstr(resource='killsText'))
) _txt(280, 4, bs.Lstr(resource='deathsText'), maxwidth=100)
_txt(180, 4, babase.Lstr(resource='killsText'))
_txt(280, 4, babase.Lstr(resource='deathsText'), maxwidth=100)
score_label = 'Score' if results is None else results.score_label score_label = 'Score' if results is None else results.score_label
translated = babase.Lstr(translate=('scoreNames', score_label)) translated = bs.Lstr(translate=('scoreNames', score_label))
_txt(390, 0, translated) _txt(390, 0, translated)
@ -191,7 +183,7 @@ class MultiTeamScoreScreenActivity(bs.ScoreScreenActivity):
topkilledcount = min(topkilledcount, prec.accum_killed_count) topkilledcount = min(topkilledcount, prec.accum_killed_count)
def _scoretxt( def _scoretxt(
text: str | babase.Lstr, text: str | bs.Lstr,
x_offs: float, x_offs: float,
highlight: bool, highlight: bool,
delay2: float, delay2: float,
@ -228,7 +220,7 @@ class MultiTeamScoreScreenActivity(bs.ScoreScreenActivity):
transition_delay=tdelay, transition_delay=tdelay,
).autoretain() ).autoretain()
Text( Text(
babase.Lstr(value=playerrec.getname(full=True)), bs.Lstr(value=playerrec.getname(full=True)),
maxwidth=160, maxwidth=160,
scale=0.75 * scale, scale=0.75 * scale,
position=( position=(
@ -237,7 +229,7 @@ class MultiTeamScoreScreenActivity(bs.ScoreScreenActivity):
), ),
h_align=Text.HAlign.LEFT, h_align=Text.HAlign.LEFT,
v_align=Text.VAlign.CENTER, v_align=Text.VAlign.CENTER,
color=babase.safecolor(playerrec.team.color + (1,)), color=bs.safecolor(playerrec.team.color + (1,)),
transition=Text.Transition.IN_LEFT, transition=Text.Transition.IN_LEFT,
transition_delay=tdelay, transition_delay=tdelay,
).autoretain() ).autoretain()

View File

@ -4,15 +4,9 @@
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING
import babase
import bascenev1 as bs import bascenev1 as bs
from bascenev1lib.activity.multiteamscore import MultiTeamScoreScreenActivity from bascenev1lib.activity.multiteamscore import MultiTeamScoreScreenActivity
if TYPE_CHECKING:
pass
class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity): class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
"""Final score screen for a team series.""" """Final score screen for a team series."""
@ -35,25 +29,25 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
from bascenev1lib.actor.text import Text from bascenev1lib.actor.text import Text
from bascenev1lib.actor.image import Image from bascenev1lib.actor.image import Image
babase.set_analytics_screen( bs.set_analytics_screen(
'FreeForAll Series Victory Screen' 'FreeForAll Series Victory Screen'
if self._is_ffa if self._is_ffa
else 'Teams Series Victory Screen' else 'Teams Series Victory Screen'
) )
assert babase.app.classic is not None assert bs.app.classic is not None
if babase.app.ui_v1.uiscale is babase.UIScale.LARGE: if bs.app.ui_v1.uiscale is bs.UIScale.LARGE:
sval = babase.Lstr(resource='pressAnyKeyButtonPlayAgainText') sval = bs.Lstr(resource='pressAnyKeyButtonPlayAgainText')
else: else:
sval = babase.Lstr(resource='pressAnyButtonPlayAgainText') sval = bs.Lstr(resource='pressAnyButtonPlayAgainText')
self._show_up_next = False self._show_up_next = False
self._custom_continue_message = sval self._custom_continue_message = sval
super().on_begin() super().on_begin()
winning_sessionteam = self.settings_raw['winner'] winning_sessionteam = self.settings_raw['winner']
# Pause a moment before playing victory music. # Pause a moment before playing victory music.
bs.timer(0.6, babase.WeakCall(self._play_victory_music)) bs.timer(0.6, bs.WeakCall(self._play_victory_music))
bs.timer( bs.timer(
4.4, babase.WeakCall(self._show_winner, self.settings_raw['winner']) 4.4, bs.WeakCall(self._show_winner, self.settings_raw['winner'])
) )
bs.timer(4.6, self._score_display_sound.play) bs.timer(4.6, self._score_display_sound.play)
@ -82,19 +76,19 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
tval = 6.4 tval = 6.4
t_incr = 0.12 t_incr = 0.12
always_use_first_to = babase.app.lang.get_resource( always_use_first_to = bs.app.lang.get_resource(
'bestOfUseFirstToInstead' 'bestOfUseFirstToInstead'
) )
session = self.session session = self.session
if self._is_ffa: if self._is_ffa:
assert isinstance(session, bs.FreeForAllSession) assert isinstance(session, bs.FreeForAllSession)
txt = babase.Lstr( txt = bs.Lstr(
value='${A}:', value='${A}:',
subs=[ subs=[
( (
'${A}', '${A}',
babase.Lstr( bs.Lstr(
resource='firstToFinalText', resource='firstToFinalText',
subs=[ subs=[
( (
@ -115,12 +109,12 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
# they're not using this language. Should try to come up # they're not using this language. Should try to come up
# with a wording that works everywhere. # with a wording that works everywhere.
if always_use_first_to: if always_use_first_to:
txt = babase.Lstr( txt = bs.Lstr(
value='${A}:', value='${A}:',
subs=[ subs=[
( (
'${A}', '${A}',
babase.Lstr( bs.Lstr(
resource='firstToFinalText', resource='firstToFinalText',
subs=[ subs=[
( (
@ -135,12 +129,12 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
], ],
) )
else: else:
txt = babase.Lstr( txt = bs.Lstr(
value='${A}:', value='${A}:',
subs=[ subs=[
( (
'${A}', '${A}',
babase.Lstr( bs.Lstr(
resource='bestOfFinalText', resource='bestOfFinalText',
subs=[ subs=[
( (
@ -173,7 +167,7 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
if not self._is_ffa: if not self._is_ffa:
Text( Text(
babase.Lstr( bs.Lstr(
resource='gamesToText', resource='gamesToText',
subs=[ subs=[
('${WINCOUNT}', str(win_score)), ('${WINCOUNT}', str(win_score)),
@ -208,7 +202,7 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
break break
if mvp is not None: if mvp is not None:
Text( Text(
babase.Lstr(resource='mostValuablePlayerText'), bs.Lstr(resource='mostValuablePlayerText'),
color=(0.5, 0.5, 0.5, 1.0), color=(0.5, 0.5, 0.5, 1.0),
v_align=Text.VAlign.CENTER, v_align=Text.VAlign.CENTER,
maxwidth=300, maxwidth=300,
@ -228,13 +222,13 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
).autoretain() ).autoretain()
assert mvp_name is not None assert mvp_name is not None
Text( Text(
babase.Lstr(value=mvp_name), bs.Lstr(value=mvp_name),
position=(280, ts_height / 2 - 55 + 15 - 5), position=(280, ts_height / 2 - 55 + 15 - 5),
h_align=Text.HAlign.LEFT, h_align=Text.HAlign.LEFT,
v_align=Text.VAlign.CENTER, v_align=Text.VAlign.CENTER,
maxwidth=170, maxwidth=170,
scale=1.3, scale=1.3,
color=babase.safecolor(mvp.team.color + (1,)), color=bs.safecolor(mvp.team.color + (1,)),
transition=Text.Transition.IN_LEFT, transition=Text.Transition.IN_LEFT,
transition_delay=tval, transition_delay=tval,
).autoretain() ).autoretain()
@ -249,7 +243,7 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
most_kills = entry[2].kill_count most_kills = entry[2].kill_count
if mvp is not None: if mvp is not None:
Text( Text(
babase.Lstr(resource='mostViolentPlayerText'), bs.Lstr(resource='mostViolentPlayerText'),
color=(0.5, 0.5, 0.5, 1.0), color=(0.5, 0.5, 0.5, 1.0),
v_align=Text.VAlign.CENTER, v_align=Text.VAlign.CENTER,
maxwidth=300, maxwidth=300,
@ -259,12 +253,12 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
transition_delay=tval, transition_delay=tval,
).autoretain() ).autoretain()
Text( Text(
babase.Lstr( bs.Lstr(
value='(${A})', value='(${A})',
subs=[ subs=[
( (
'${A}', '${A}',
babase.Lstr( bs.Lstr(
resource='killsTallyText', resource='killsTallyText',
subs=[('${COUNT}', str(most_kills))], subs=[('${COUNT}', str(most_kills))],
), ),
@ -289,12 +283,12 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
).autoretain() ).autoretain()
assert mvp_name is not None assert mvp_name is not None
Text( Text(
babase.Lstr(value=mvp_name), bs.Lstr(value=mvp_name),
position=(270, ts_height / 2 - 150 - 30 - 36 + v_extra + 15), position=(270, ts_height / 2 - 150 - 30 - 36 + v_extra + 15),
h_align=Text.HAlign.LEFT, h_align=Text.HAlign.LEFT,
v_align=Text.VAlign.CENTER, v_align=Text.VAlign.CENTER,
maxwidth=180, maxwidth=180,
color=babase.safecolor(mvp.team.color + (1,)), color=bs.safecolor(mvp.team.color + (1,)),
transition=Text.Transition.IN_LEFT, transition=Text.Transition.IN_LEFT,
transition_delay=tval, transition_delay=tval,
).autoretain() ).autoretain()
@ -310,7 +304,7 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
most_killed = entry[2].killed_count most_killed = entry[2].killed_count
if mkp is not None: if mkp is not None:
Text( Text(
babase.Lstr(resource='mostViolatedPlayerText'), bs.Lstr(resource='mostViolatedPlayerText'),
color=(0.5, 0.5, 0.5, 1.0), color=(0.5, 0.5, 0.5, 1.0),
v_align=Text.VAlign.CENTER, v_align=Text.VAlign.CENTER,
maxwidth=300, maxwidth=300,
@ -320,12 +314,12 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
transition_delay=tval, transition_delay=tval,
).autoretain() ).autoretain()
Text( Text(
babase.Lstr( bs.Lstr(
value='(${A})', value='(${A})',
subs=[ subs=[
( (
'${A}', '${A}',
babase.Lstr( bs.Lstr(
resource='deathsTallyText', resource='deathsTallyText',
subs=[('${COUNT}', str(most_killed))], subs=[('${COUNT}', str(most_killed))],
), ),
@ -349,11 +343,11 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
).autoretain() ).autoretain()
assert mkp_name is not None assert mkp_name is not None
Text( Text(
babase.Lstr(value=mkp_name), bs.Lstr(value=mkp_name),
position=(270, ts_height / 2 - 300 - 30 - 36 + v_extra + 15), position=(270, ts_height / 2 - 300 - 30 - 36 + v_extra + 15),
h_align=Text.HAlign.LEFT, h_align=Text.HAlign.LEFT,
v_align=Text.VAlign.CENTER, v_align=Text.VAlign.CENTER,
color=babase.safecolor(mkp.team.color + (1,)), color=bs.safecolor(mkp.team.color + (1,)),
maxwidth=180, maxwidth=180,
transition=Text.Transition.IN_LEFT, transition=Text.Transition.IN_LEFT,
transition_delay=tval, transition_delay=tval,
@ -363,7 +357,7 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
# Now show individual scores. # Now show individual scores.
tdelay = tval tdelay = tval
Text( Text(
babase.Lstr(resource='finalScoresText'), bs.Lstr(resource='finalScoresText'),
color=(0.5, 0.5, 0.5, 1.0), color=(0.5, 0.5, 0.5, 1.0),
position=(ts_h_offs, ts_height / 2), position=(ts_h_offs, ts_height / 2),
transition=Text.Transition.IN_RIGHT, transition=Text.Transition.IN_RIGHT,
@ -396,17 +390,17 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
transition_delay=tdelay, transition_delay=tdelay,
).autoretain() ).autoretain()
Text( Text(
babase.Lstr(value=name), bs.Lstr(value=name),
position=(ts_h_offs - 50, ts_height / 2 + v_offs + 15), position=(ts_h_offs - 50, ts_height / 2 + v_offs + 15),
h_align=Text.HAlign.LEFT, h_align=Text.HAlign.LEFT,
v_align=Text.VAlign.CENTER, v_align=Text.VAlign.CENTER,
maxwidth=180, maxwidth=180,
color=babase.safecolor(prec.team.color + (1,)), color=bs.safecolor(prec.team.color + (1,)),
transition=Text.Transition.IN_RIGHT, transition=Text.Transition.IN_RIGHT,
transition_delay=tdelay, transition_delay=tdelay,
).autoretain() ).autoretain()
bs.timer(15.0, babase.WeakCall(self._show_tips)) bs.timer(15.0, bs.WeakCall(self._show_tips))
def _show_tips(self) -> None: def _show_tips(self) -> None:
from bascenev1lib.actor.tipstext import TipsText from bascenev1lib.actor.tipstext import TipsText
@ -443,7 +437,7 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
assert i.node assert i.node
bs.animate(i.node, 'opacity', {0.0: 0.0, 0.25: 1.0}) bs.animate(i.node, 'opacity', {0.0: 0.0, 0.25: 1.0})
ZoomText( ZoomText(
babase.Lstr( bs.Lstr(
value=team.players[0].getname(full=True, icon=False) value=team.players[0].getname(full=True, icon=False)
), ),
position=(0, 97 + offs_v), position=(0, 97 + offs_v),
@ -460,7 +454,7 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
wins_resource = 'seriesWinLine1PlayerText' wins_resource = 'seriesWinLine1PlayerText'
else: else:
wins_resource = 'seriesWinLine1TeamText' wins_resource = 'seriesWinLine1TeamText'
wins_text = babase.Lstr(resource=wins_resource) wins_text = bs.Lstr(resource=wins_resource)
# Temp - if these come up as the english default, fall-back to the # Temp - if these come up as the english default, fall-back to the
# unified old form which is more likely to be translated. # unified old form which is more likely to be translated.
@ -473,7 +467,7 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
maxwidth=250, maxwidth=250,
).autoretain() ).autoretain()
ZoomText( ZoomText(
babase.Lstr(resource='seriesWinLine2Text'), bs.Lstr(resource='seriesWinLine2Text'),
position=(0, -110 + offs_v), position=(0, -110 + offs_v),
scale=1.0 * s_extra, scale=1.0 * s_extra,
color=team.color, color=team.color,

View File

@ -6,7 +6,6 @@ from __future__ import annotations
from typing import TYPE_CHECKING, TypeVar, overload from typing import TYPE_CHECKING, TypeVar, overload
import babase
import bascenev1 as bs import bascenev1 as bs
from bascenev1lib.actor.spaz import Spaz from bascenev1lib.actor.spaz import Spaz
@ -99,7 +98,7 @@ class PlayerSpaz(Spaz):
player: Any = self._player player: Any = self._player
assert isinstance(player, playertype) assert isinstance(player, playertype)
if not player.exists() and doraise: if not player.exists() and doraise:
raise babase.PlayerNotFoundError() raise bs.PlayerNotFoundError()
return player if player.exists() else None return player if player.exists() else None
def connect_controls_to_player( def connect_controls_to_player(
@ -129,16 +128,16 @@ class PlayerSpaz(Spaz):
else: else:
player.resetinput() player.resetinput()
player.assigninput(babase.InputType.UP_DOWN, self.on_move_up_down) player.assigninput(bs.InputType.UP_DOWN, self.on_move_up_down)
player.assigninput(babase.InputType.LEFT_RIGHT, self.on_move_left_right) player.assigninput(bs.InputType.LEFT_RIGHT, self.on_move_left_right)
player.assigninput( player.assigninput(
babase.InputType.HOLD_POSITION_PRESS, self.on_hold_position_press bs.InputType.HOLD_POSITION_PRESS, self.on_hold_position_press
) )
player.assigninput( player.assigninput(
babase.InputType.HOLD_POSITION_RELEASE, bs.InputType.HOLD_POSITION_RELEASE,
self.on_hold_position_release, self.on_hold_position_release,
) )
intp = babase.InputType intp = bs.InputType
if enable_jump: if enable_jump:
player.assigninput(intp.JUMP_PRESS, self.on_jump_press) player.assigninput(intp.JUMP_PRESS, self.on_jump_press)
player.assigninput(intp.JUMP_RELEASE, self.on_jump_release) player.assigninput(intp.JUMP_RELEASE, self.on_jump_release)
@ -210,7 +209,7 @@ class PlayerSpaz(Spaz):
picked_up_by = msg.node.source_player picked_up_by = msg.node.source_player
if picked_up_by: if picked_up_by:
self.last_player_attacked_by = picked_up_by self.last_player_attacked_by = picked_up_by
self.last_attacked_time = babase.apptime() self.last_attacked_time = bs.apptime()
self.last_attacked_type = ('picked_up', 'default') self.last_attacked_type = ('picked_up', 'default')
elif isinstance(msg, bs.StandMessage): elif isinstance(msg, bs.StandMessage):
super().handlemessage(msg) # Augment standard behavior. super().handlemessage(msg) # Augment standard behavior.
@ -248,7 +247,7 @@ class PlayerSpaz(Spaz):
# something like last_actor_attacked_by to fix that. # something like last_actor_attacked_by to fix that.
if ( if (
self.last_player_attacked_by self.last_player_attacked_by
and babase.apptime() - self.last_attacked_time < 4.0 and bs.apptime() - self.last_attacked_time < 4.0
): ):
killerplayer = self.last_player_attacked_by killerplayer = self.last_player_attacked_by
else: else:
@ -279,7 +278,7 @@ class PlayerSpaz(Spaz):
source_player = msg.get_source_player(type(self._player)) source_player = msg.get_source_player(type(self._player))
if source_player: if source_player:
self.last_player_attacked_by = source_player self.last_player_attacked_by = source_player
self.last_attacked_time = babase.apptime() self.last_attacked_time = bs.apptime()
self.last_attacked_type = (msg.hit_type, msg.hit_subtype) self.last_attacked_type = (msg.hit_type, msg.hit_subtype)
super().handlemessage(msg) # Augment standard behavior. super().handlemessage(msg) # Augment standard behavior.
activity = self._activity() activity = self._activity()

View File

@ -7,7 +7,6 @@ from __future__ import annotations
import random import random
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import babase
import bascenev1 as bs import bascenev1 as bs
if TYPE_CHECKING: if TYPE_CHECKING:
@ -22,7 +21,7 @@ class PopupText(bs.Actor):
def __init__( def __init__(
self, self,
text: str | babase.Lstr, text: str | bs.Lstr,
position: Sequence[float] = (0.0, 0.0, 0.0), position: Sequence[float] = (0.0, 0.0, 0.0),
color: Sequence[float] = (1.0, 1.0, 1.0, 1.0), color: Sequence[float] = (1.0, 1.0, 1.0, 1.0),
random_offset: float = 0.5, random_offset: float = 0.5,
@ -116,7 +115,7 @@ class PopupText(bs.Actor):
# kill ourself # kill ourself
self._die_timer = bs.Timer( self._die_timer = bs.Timer(
lifespan, babase.WeakCall(self.handlemessage, bs.DieMessage()) lifespan, bs.WeakCall(self.handlemessage, bs.DieMessage())
) )
def handlemessage(self, msg: Any) -> Any: def handlemessage(self, msg: Any) -> Any:

View File

@ -5,14 +5,9 @@
from __future__ import annotations from __future__ import annotations
import weakref import weakref
from typing import TYPE_CHECKING
import babase
import bascenev1 as bs import bascenev1 as bs
if TYPE_CHECKING:
pass
class RespawnIcon: class RespawnIcon:
"""An icon with a countdown that appears alongside the screen. """An icon with a countdown that appears alongside the screen.
@ -22,11 +17,11 @@ class RespawnIcon:
This is used to indicate that a bascenev1.Player is waiting to respawn. This is used to indicate that a bascenev1.Player is waiting to respawn.
""" """
_MASKTEXSTORENAME = babase.storagename('masktex') _MASKTEXSTORENAME = bs.storagename('masktex')
_ICONSSTORENAME = babase.storagename('icons') _ICONSSTORENAME = bs.storagename('icons')
def __init__(self, player: bs.Player, respawn_time: float): def __init__(self, player: bs.Player, respawn_time: float):
"""Instantiate with a bascenev1.Player and respawn_time (in seconds).""" """Instantiate with a Player and respawn_time (in seconds)."""
self._visible = True self._visible = True
on_right, offs_extra, respawn_icons = self._get_context(player) on_right, offs_extra, respawn_icons = self._get_context(player)
@ -81,13 +76,13 @@ class RespawnIcon:
attrs={ attrs={
'v_attach': 'top', 'v_attach': 'top',
'h_attach': 'right' if on_right else 'left', 'h_attach': 'right' if on_right else 'left',
'text': babase.Lstr(value=player.getname()), 'text': bs.Lstr(value=player.getname()),
'maxwidth': 100, 'maxwidth': 100,
'h_align': 'center', 'h_align': 'center',
'v_align': 'center', 'v_align': 'center',
'shadow': 1.0, 'shadow': 1.0,
'flatness': 1.0, 'flatness': 1.0,
'color': babase.safecolor(icon['tint_color']), 'color': bs.safecolor(icon['tint_color']),
'scale': 0.5, 'scale': 0.5,
'position': npos, 'position': npos,
}, },
@ -109,7 +104,7 @@ class RespawnIcon:
'shadow': 0.5, 'shadow': 0.5,
'flatness': 0.5, 'flatness': 0.5,
'v_attach': 'top', 'v_attach': 'top',
'color': babase.safecolor(icon['tint_color']), 'color': bs.safecolor(icon['tint_color']),
'text': '', 'text': '',
}, },
) )
@ -118,10 +113,10 @@ class RespawnIcon:
assert self._text.node assert self._text.node
bs.animate(self._text.node, 'scale', {0: 0, 0.1: 0.9}) bs.animate(self._text.node, 'scale', {0: 0, 0.1: 0.9})
self._respawn_time = babase.apptime() + respawn_time self._respawn_time = bs.time() + respawn_time
self._update() self._update()
self._timer: bs.Timer | None = bs.Timer( self._timer: bs.Timer | None = bs.Timer(
1.0, babase.WeakCall(self._update), repeat=True 1.0, bs.WeakCall(self._update), repeat=True
) )
@property @property
@ -159,7 +154,7 @@ class RespawnIcon:
return on_right, offs_extra, icons return on_right, offs_extra, icons
def _update(self) -> None: def _update(self) -> None:
remaining = int(round(self._respawn_time - babase.apptime())) remaining = int(round(self._respawn_time - bs.time()))
if remaining > 0: if remaining > 0:
assert self._text is not None assert self._text is not None
if self._text.node: if self._text.node:

View File

@ -7,7 +7,6 @@ from __future__ import annotations
from enum import Enum from enum import Enum
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import babase
import bascenev1 as bs import bascenev1 as bs
if TYPE_CHECKING: if TYPE_CHECKING:
@ -56,7 +55,7 @@ class Text(bs.Actor):
def __init__( def __init__(
self, self,
text: str | babase.Lstr, text: str | bs.Lstr,
position: tuple[float, float] = (0.0, 0.0), position: tuple[float, float] = (0.0, 0.0),
h_align: HAlign = HAlign.LEFT, h_align: HAlign = HAlign.LEFT,
v_align: VAlign = VAlign.NONE, v_align: VAlign = VAlign.NONE,
@ -219,7 +218,7 @@ class Text(bs.Actor):
if transition_out_delay is not None: if transition_out_delay is not None:
bs.timer( bs.timer(
transition_delay + transition_out_delay + 1.0, transition_delay + transition_out_delay + 1.0,
babase.WeakCall(self.handlemessage, bs.DieMessage()), bs.WeakCall(self.handlemessage, bs.DieMessage()),
) )
def handlemessage(self, msg: Any) -> Any: def handlemessage(self, msg: Any) -> Any:

View File

@ -6,7 +6,6 @@ from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import babase
import bascenev1 as bs import bascenev1 as bs
if TYPE_CHECKING: if TYPE_CHECKING:
@ -34,8 +33,8 @@ class TipsText(bs.Actor):
'v_attach': 'bottom', 'v_attach': 'bottom',
}, },
) )
tval = babase.Lstr( tval = bs.Lstr(
value='${A}:', subs=[('${A}', babase.Lstr(resource='tipText'))] value='${A}:', subs=[('${A}', bs.Lstr(resource='tipText'))]
) )
self.title_node = bs.newnode( self.title_node = bs.newnode(
'text', 'text',
@ -54,7 +53,7 @@ class TipsText(bs.Actor):
self._message_spacing = 3000 self._message_spacing = 3000
self._change_timer = bs.Timer( self._change_timer = bs.Timer(
0.001 * (self._message_duration + self._message_spacing), 0.001 * (self._message_duration + self._message_spacing),
babase.WeakCall(self.change_phrase), bs.WeakCall(self.change_phrase),
repeat=True, repeat=True,
) )
self._combine = bs.newnode( self._combine = bs.newnode(
@ -70,11 +69,11 @@ class TipsText(bs.Actor):
"""Switch the visible tip phrase.""" """Switch the visible tip phrase."""
from babase import get_remote_app_name from babase import get_remote_app_name
next_tip = babase.Lstr( next_tip = bs.Lstr(
translate=( translate=(
'tips', 'tips',
babase.app.classic.get_next_tip() bs.app.classic.get_next_tip()
if babase.app.classic is not None if bs.app.classic is not None
else '', else '',
), ),
subs=[('${REMOTE_APP_NAME}', get_remote_app_name())], subs=[('${REMOTE_APP_NAME}', get_remote_app_name())],

View File

@ -5,9 +5,9 @@
from __future__ import annotations from __future__ import annotations
import random import random
import logging
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import babase
import bascenev1 as bs import bascenev1 as bs
if TYPE_CHECKING: if TYPE_CHECKING:
@ -24,7 +24,7 @@ class ZoomText(bs.Actor):
def __init__( def __init__(
self, self,
text: str | babase.Lstr, text: str | bs.Lstr,
position: tuple[float, float] = (0.0, 0.0), position: tuple[float, float] = (0.0, 0.0),
shiftposition: tuple[float, float] | None = None, shiftposition: tuple[float, float] | None = None,
shiftdelay: float | None = None, shiftdelay: float | None = None,
@ -47,7 +47,7 @@ class ZoomText(bs.Actor):
if shiftdelay is None: if shiftdelay is None:
shiftdelay = 2.500 shiftdelay = 2.500
if shiftdelay < 0.0: if shiftdelay < 0.0:
babase.print_error('got shiftdelay < 0') logging.error('got shiftdelay < 0')
shiftdelay = 0.0 shiftdelay = 0.0
self._project_scale = project_scale self._project_scale = project_scale
self.node = bs.newnode( self.node = bs.newnode(
@ -69,7 +69,7 @@ class ZoomText(bs.Actor):
) )
# we never jitter in vr mode.. # we never jitter in vr mode..
if babase.app.vr_mode: if bs.app.vr_mode:
jitter = 0.0 jitter = 0.0
# if they want jitter, animate its position slightly... # if they want jitter, animate its position slightly...
@ -82,14 +82,12 @@ class ZoomText(bs.Actor):
positionadjusted2 = (shiftposition[0], shiftposition[1] - 100) positionadjusted2 = (shiftposition[0], shiftposition[1] - 100)
bs.timer( bs.timer(
shiftdelay, shiftdelay,
babase.WeakCall( bs.WeakCall(self._shift, positionadjusted, positionadjusted2),
self._shift, positionadjusted, positionadjusted2
),
) )
if jitter > 0.0: if jitter > 0.0:
bs.timer( bs.timer(
shiftdelay + 0.25, shiftdelay + 0.25,
babase.WeakCall( bs.WeakCall(
self._jitter, positionadjusted2, jitter * scale self._jitter, positionadjusted2, jitter * scale
), ),
) )
@ -158,9 +156,7 @@ class ZoomText(bs.Actor):
# if they give us a lifespan, kill ourself down the line # if they give us a lifespan, kill ourself down the line
if lifespan is not None: if lifespan is not None:
bs.timer( bs.timer(lifespan, bs.WeakCall(self.handlemessage, bs.DieMessage()))
lifespan, babase.WeakCall(self.handlemessage, bs.DieMessage())
)
def handlemessage(self, msg: Any) -> Any: def handlemessage(self, msg: Any) -> Any:
assert not self.expired assert not self.expired

View File

@ -6,7 +6,6 @@ from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import babase
import bascenev1 as bs import bascenev1 as bs
if TYPE_CHECKING: if TYPE_CHECKING:
@ -23,7 +22,7 @@ class SharedObjects:
standard materials. standard materials.
""" """
_STORENAME = babase.storagename() _STORENAME = bs.storagename()
def __init__(self) -> None: def __init__(self) -> None:
activity = bs.getactivity() activity = bs.getactivity()

View File

@ -4,14 +4,8 @@
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING
import babase
import bascenev1 as bs import bascenev1 as bs
if TYPE_CHECKING:
pass
# FIXME: Could change this to be a classmethod of session types? # FIXME: Could change this to be a classmethod of session types?
class PlaylistTypeVars: class PlaylistTypeVars:
@ -26,26 +20,26 @@ class PlaylistTypeVars:
self.sessiontype: type[bs.Session] self.sessiontype: type[bs.Session]
if issubclass(sessiontype, bs.DualTeamSession): if issubclass(sessiontype, bs.DualTeamSession):
play_mode_name = babase.Lstr( play_mode_name = bs.Lstr(
resource='playModes.teamsText', fallback_resource='teamsText' resource='playModes.teamsText', fallback_resource='teamsText'
) )
self.get_default_list_call = get_default_teams_playlist self.get_default_list_call = get_default_teams_playlist
self.session_type_name = 'bascenev1.DualTeamSession' self.session_type_name = 'bascenev1.DualTeamSession'
self.config_name = 'Team Tournament' self.config_name = 'Team Tournament'
self.window_title_name = babase.Lstr( self.window_title_name = bs.Lstr(
resource='playModes.teamsText', fallback_resource='teamsText' resource='playModes.teamsText', fallback_resource='teamsText'
) )
self.sessiontype = bs.DualTeamSession self.sessiontype = bs.DualTeamSession
elif issubclass(sessiontype, bs.FreeForAllSession): elif issubclass(sessiontype, bs.FreeForAllSession):
play_mode_name = babase.Lstr( play_mode_name = bs.Lstr(
resource='playModes.freeForAllText', resource='playModes.freeForAllText',
fallback_resource='freeForAllText', fallback_resource='freeForAllText',
) )
self.get_default_list_call = get_default_free_for_all_playlist self.get_default_list_call = get_default_free_for_all_playlist
self.session_type_name = 'bascenev1.FreeForAllSession' self.session_type_name = 'bascenev1.FreeForAllSession'
self.config_name = 'Free-for-All' self.config_name = 'Free-for-All'
self.window_title_name = babase.Lstr( self.window_title_name = bs.Lstr(
resource='playModes.freeForAllText', resource='playModes.freeForAllText',
fallback_resource='freeForAllText', fallback_resource='freeForAllText',
) )
@ -55,11 +49,11 @@ class PlaylistTypeVars:
raise RuntimeError( raise RuntimeError(
f'Playlist type vars undefined for sessiontype: {sessiontype}' f'Playlist type vars undefined for sessiontype: {sessiontype}'
) )
self.default_list_name = babase.Lstr( self.default_list_name = bs.Lstr(
resource='defaultGameListNameText', resource='defaultGameListNameText',
subs=[('${PLAYMODE}', play_mode_name)], subs=[('${PLAYMODE}', play_mode_name)],
) )
self.default_new_list_name = babase.Lstr( self.default_new_list_name = bs.Lstr(
resource='defaultNewGameListNameText', resource='defaultNewGameListNameText',
subs=[('${PLAYMODE}', play_mode_name)], subs=[('${PLAYMODE}', play_mode_name)],
) )

View File

@ -6,7 +6,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import babase import bauiv1 as bui
from bauiv1lib.settings.testing import TestingWindow from bauiv1lib.settings.testing import TestingWindow
if TYPE_CHECKING: if TYPE_CHECKING:
@ -18,10 +18,10 @@ class VRTestingWindow(TestingWindow):
def __init__(self, transition: str = 'in_right'): def __init__(self, transition: str = 'in_right'):
entries: list[dict[str, Any]] = [] entries: list[dict[str, Any]] = []
app = babase.app app = bui.app
assert app.classic is not None assert app.classic is not None
# these are gear-vr only # These are gear-vr only.
if ( if (
app.classic.platform == 'android' app.classic.platform == 'android'
and app.classic.subplatform == 'oculus' and app.classic.subplatform == 'oculus'
@ -44,16 +44,19 @@ class VRTestingWindow(TestingWindow):
}, },
# {'name':'eyeOffsX','label':'Eye IPD','increment':0.001} # {'name':'eyeOffsX','label':'Eye IPD','increment':0.001}
] ]
# cardboard/gearvr get eye offset controls..
# Cardboard/gearvr get eye offset controls.
# if app.platform == 'android': # if app.platform == 'android':
# entries += [ # entries += [
# {'name':'eyeOffsY','label':'Eye Offset Y','increment':0.01}, # {'name':'eyeOffsY','label':'Eye Offset Y','increment':0.01},
# {'name':'eyeOffsZ','label':'Eye Offset Z','increment':0.005}] # {'name':'eyeOffsZ','label':'Eye Offset Z','increment':0.005}]
# everyone gets head-scale
# Everyone gets head-scale.
entries += [ entries += [
{'name': 'headScale', 'label': 'Head Scale', 'increment': 1.0} {'name': 'headScale', 'label': 'Head Scale', 'increment': 1.0}
] ]
# and everyone gets all these..
# And everyone gets all these.
entries += [ entries += [
{ {
'name': 'vrCamOffsetY', 'name': 'vrCamOffsetY',
@ -88,7 +91,7 @@ class VRTestingWindow(TestingWindow):
] ]
super().__init__( super().__init__(
babase.Lstr(resource='settingsWindowAdvanced.vrTestingText'), bui.Lstr(resource='settingsWindowAdvanced.vrTestingText'),
entries, entries,
transition, transition,
) )

View File

@ -7,7 +7,6 @@ from __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
from typing import TYPE_CHECKING, TypeVar, Generic from typing import TYPE_CHECKING, TypeVar, Generic
import babase
import bauiv1 as bui import bauiv1 as bui
if TYPE_CHECKING: if TYPE_CHECKING:
@ -35,7 +34,7 @@ class TabRow(Generic[T]):
def __init__( def __init__(
self, self,
parent: bui.Widget, parent: bui.Widget,
tabdefs: list[tuple[T, babase.Lstr]], tabdefs: list[tuple[T, bui.Lstr]],
pos: tuple[float, float], pos: tuple[float, float],
size: tuple[float, float], size: tuple[float, float],
on_select_call: Callable[[T], None] | None = None, on_select_call: Callable[[T], None] | None = None,
@ -58,7 +57,7 @@ class TabRow(Generic[T]):
size=size, size=size,
label=tab_label, label=tab_label,
enable_sound=False, enable_sound=False,
on_activate_call=babase.Call( on_activate_call=bui.Call(
self._tick_and_call, on_select_call, tab_id self._tick_and_call, on_select_call, tab_id
), ),
) )

View File

@ -45,7 +45,7 @@ void StressTest::Update() {
if (t - last_stress_test_update_time_ >= 10000) { if (t - last_stress_test_update_time_ >= 10000) {
if (stress_test_stats_file_ == nullptr) { if (stress_test_stats_file_ == nullptr) {
assert(g_core); assert(g_core);
auto user_python_dir = g_core->platform->GetUserPythonDirectory(); auto user_python_dir = g_core->GetUserPythonDirectory();
if (user_python_dir) { if (user_python_dir) {
std::string f_name = *user_python_dir + "/stress_test_stats.csv"; std::string f_name = *user_python_dir + "/stress_test_stats.csv";
stress_test_stats_file_ = stress_test_stats_file_ =

View File

@ -39,7 +39,7 @@ static const bool kShowPruningInfo = false;
#define PENDING_LOAD_PROCESS_TIME 5 #define PENDING_LOAD_PROCESS_TIME 5
Assets::Assets() { Assets::Assets() {
asset_paths_.emplace_back(g_core->platform->GetDataDirectory() + BA_DIRSLASH asset_paths_.emplace_back(g_core->GetDataDirectory() + BA_DIRSLASH
+ "ba_data"); + "ba_data");
for (bool& have_pending_load : have_pending_loads_) { for (bool& have_pending_load : have_pending_loads_) {
have_pending_load = false; have_pending_load = false;

View File

@ -86,7 +86,7 @@ void BaseFeatureSet::OnModuleExec(PyObject* module) {
// Want to run this at the last possible moment before spinning up our // Want to run this at the last possible moment before spinning up our
// BaseFeatureSet. This locks in baenv customizations. // BaseFeatureSet. This locks in baenv customizations.
g_core->python->ApplyBaEnvConfig(); g_core->ApplyBaEnvConfig();
// Create our feature-set's C++ front-end. // Create our feature-set's C++ front-end.
assert(g_base == nullptr); assert(g_base == nullptr);

View File

@ -826,8 +826,8 @@ void TextGraphics::LoadGlyphPage(uint32_t index) {
if (g_glyph_pages[index] == nullptr) { if (g_glyph_pages[index] == nullptr) {
char buffer[256]; char buffer[256];
snprintf(buffer, sizeof(buffer), "%s%sba_data%sfonts%sfontSmall%d.fdata", snprintf(buffer, sizeof(buffer), "%s%sba_data%sfonts%sfontSmall%d.fdata",
g_core->platform->GetDataDirectory().c_str(), BA_DIRSLASH, g_core->GetDataDirectory().c_str(), BA_DIRSLASH, BA_DIRSLASH,
BA_DIRSLASH, BA_DIRSLASH, index); BA_DIRSLASH, index);
FILE* f = g_core->platform->FOpen(buffer, "rb"); FILE* f = g_core->platform->FOpen(buffer, "rb");
BA_PRECONDITION(f); BA_PRECONDITION(f);
BA_PRECONDITION(sizeof(TextGraphics::Glyph[2]) == sizeof(float[18])); BA_PRECONDITION(sizeof(TextGraphics::Glyph[2]) == sizeof(float[18]));

View File

@ -711,12 +711,9 @@ static auto PyEnv(PyObject* self) -> PyObject* {
default: default:
throw Exception(); throw Exception();
} }
std::optional<std::string> user_py_dir = std::optional<std::string> user_py_dir = g_core->GetUserPythonDirectory();
g_core->platform->GetUserPythonDirectory(); std::optional<std::string> app_py_dir = g_core->GetAppPythonDirectory();
std::optional<std::string> app_py_dir = std::optional<std::string> site_py_dir = g_core->GetSitePythonDirectory();
g_core->platform->GetAppPythonDirectory();
std::optional<std::string> site_py_dir =
g_core->platform->GetSitePythonDirectory();
// clang-format off // clang-format off
PyObject* env = Py_BuildValue( PyObject* env = Py_BuildValue(
@ -772,7 +769,7 @@ static auto PyEnv(PyObject* self) -> PyObject* {
"device_name", "device_name",
g_core->platform->GetDeviceName().c_str(), g_core->platform->GetDeviceName().c_str(),
"data_directory", "data_directory",
g_core->platform->GetDataDirectory().c_str()); g_core->GetDataDirectory().c_str());
// clang-format on // clang-format on
g_base->python->StoreEnv(env); g_base->python->StoreEnv(env);
} }
@ -1449,6 +1446,29 @@ static PyMethodDef PyEmptyAppModeHandleIntentExecDef = {
"(internal)", "(internal)",
}; };
// ---------------------- get_immediate_return_code ----------------------------
static auto PyGetImmediateReturnCode(PyObject* self) -> PyObject* {
BA_PYTHON_TRY;
BA_PRECONDITION(g_core);
auto val = g_core->core_config().immediate_return_code;
if (!val.has_value()) {
Py_RETURN_NONE;
}
return PyLong_FromLong(*val);
BA_PYTHON_CATCH;
}
static PyMethodDef PyGetImmediateReturnCodeDef = {
"get_immediate_return_code", // name
(PyCFunction)PyGetImmediateReturnCode, // method
METH_NOARGS, // flags
"get_immediate_return_code() -> int | None\n"
"\n"
"(internal)\n",
};
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
auto PythonMethodsApp::GetMethods() -> std::vector<PyMethodDef> { auto PythonMethodsApp::GetMethods() -> std::vector<PyMethodDef> {
@ -1496,6 +1516,7 @@ auto PythonMethodsApp::GetMethods() -> std::vector<PyMethodDef> {
PyEmptyAppModeDeactivateDef, PyEmptyAppModeDeactivateDef,
PyEmptyAppModeHandleIntentDefaultDef, PyEmptyAppModeHandleIntentDefaultDef,
PyEmptyAppModeHandleIntentExecDef, PyEmptyAppModeHandleIntentExecDef,
PyGetImmediateReturnCodeDef,
}; };
} }

View File

@ -854,7 +854,7 @@ static PyMethodDef PySetPlatformMiscReadValsDef = {
static auto PyGetLogFilePath(PyObject* self, PyObject* args) -> PyObject* { static auto PyGetLogFilePath(PyObject* self, PyObject* args) -> PyObject* {
BA_PYTHON_TRY; BA_PYTHON_TRY;
std::string config_dir = g_core->platform->GetConfigDirectory(); std::string config_dir = g_core->GetConfigDirectory();
std::string logpath = config_dir + BA_DIRSLASH + "log.json"; std::string logpath = config_dir + BA_DIRSLASH + "log.json";
return PyUnicode_FromString(logpath.c_str()); return PyUnicode_FromString(logpath.c_str());
BA_PYTHON_CATCH; BA_PYTHON_CATCH;

View File

@ -15,21 +15,46 @@ CoreFeatureSet* g_core{};
BaseSoftInterface* g_base_soft{}; BaseSoftInterface* g_base_soft{};
auto CoreFeatureSet::Import(const CoreConfig* config) -> CoreFeatureSet* { auto CoreFeatureSet::Import(const CoreConfig* config) -> CoreFeatureSet* {
// Only accept a config in monolithic builds if this is the first import. // In monolithic builds we can accept an explicit core-config the first
if (config != nullptr) { // time we're imported.
if (!g_buildconfig.monolithic_build()) { if (g_buildconfig.monolithic_build()) {
FatalError("CoreConfig can only be passed in monolithic builds."); if (config != nullptr) {
} if (g_core != nullptr) {
if (g_core != nullptr) { FatalError(
FatalError("CoreConfig can only be passed on the first import call."); "CoreConfig can only be passed on the first CoreFeatureSet::Import "
} "call.");
}
if (g_core == nullptr) { if (g_core == nullptr) {
DoImport(*config); DoImport(*config);
}
} else {
// No config passed; use a default.
if (g_core == nullptr) {
DoImport({});
}
} }
} else { } else {
// In modular builds we autogenerate a CoreConfig that takes into
// account only env-vars (or env-vars plus Python args if we're being
// run via the baenv script).
if (config != nullptr) {
FatalError("CoreConfig can't be explicitly passed in modular builds.");
}
if (g_core == nullptr) { if (g_core == nullptr) {
DoImport({}); bool can_do_args = CorePython::WasModularMainCalled();
if (can_do_args) {
// Wrangle Python's sys.argv into a standard C-style argc/argv so we
// can pass to the same handler as the monolithic C route. Note that
// a few of the values we parse here (--command, etc) have already
// been handled at the Python layer, but we parse them here just the
// same so that we have uniform records and invalid-value handling
// between monolithic and modular.
std::vector<std::string> argbuffer;
std::vector<char*> argv = CorePython::FetchPythonArgs(&argbuffer);
DoImport(CoreConfig::ForArgsAndEnvVars(argv.size(), argv.data()));
} else {
DoImport(CoreConfig::ForEnvVars());
}
} }
} }
return g_core; return g_core;
@ -90,14 +115,88 @@ void CoreFeatureSet::PostInit() {
// (so that our log handling system is fully bootstrapped), but // (so that our log handling system is fully bootstrapped), but
// technically we can push our log calls out to Python any time now since // technically we can push our log calls out to Python any time now since
// we grabbed the logging calls above. Do so immediately here if asked. // we grabbed the logging calls above. Do so immediately here if asked.
if (!g_core->core_config().hold_early_logs) { if (!core_config_.hold_early_logs) {
python->EnablePythonLoggingCalls(); python->EnablePythonLoggingCalls();
} }
}
// FIXME: MOVE THIS TO A RUN_APP_TO_COMPLETION() SORT OF PLACE. auto CoreFeatureSet::core_config() const -> const CoreConfig& {
// For now it does the right thing here since all we have is monolithic // Try to make a bit of noise if we're accessed in modular builds before
// builds but this will need to account for more situations later. // baenv values are set, since in that case we won't yet have our final
// python->ReleaseMainThreadGIL(); // core-config values. Though we want to keep this to a minimal printf so
// we don't interfere with low-level stuff like FatalError handling that
// might need core_config access at any time.
if (!g_buildconfig.monolithic_build()) {
if (!HaveBaEnvVals()) {
static bool did_warn = false;
if (!did_warn) {
did_warn = true;
printf(
"WARNING: accessing core_config() before baenv values have been "
"applied to it.\n");
}
}
}
return core_config_;
}
void CoreFeatureSet::ApplyBaEnvConfig() {
auto envcfg =
python->objs().Get(core::CorePython::ObjID::kBaEnvGetConfigCall).Call();
BA_PRECONDITION_FATAL(envcfg.Exists());
assert(!have_ba_env_vals_);
have_ba_env_vals_ = true;
// Grab everything baenv shipped us.
ba_env_config_dir_ = envcfg.GetAttr("config_dir").ValueAsString();
ba_env_data_dir_ = envcfg.GetAttr("data_dir").ValueAsString();
ba_env_app_python_dir_ =
envcfg.GetAttr("app_python_dir").ValueAsOptionalString();
ba_env_user_python_dir_ =
envcfg.GetAttr("user_python_dir").ValueAsOptionalString();
ba_env_site_python_dir_ =
envcfg.GetAttr("site_python_dir").ValueAsOptionalString();
// Consider app-python-dir to be 'custom' if baenv provided a value for it
// AND that value differs from baenv's default.
auto standard_app_python_dir =
envcfg.GetAttr("standard_app_python_dir").ValueAsString();
using_custom_app_python_dir_ =
ba_env_app_python_dir_.has_value()
&& *ba_env_app_python_dir_ != standard_app_python_dir;
// Ok, now look for the existence of ba_data in the dir we've got.
auto fullpath = ba_env_data_dir_ + BA_DIRSLASH + "ba_data";
if (!platform->FilePathExists(fullpath)) {
FatalError("ba_data directory not found at '" + fullpath + "'.");
}
}
auto CoreFeatureSet::GetAppPythonDirectory() -> std::optional<std::string> {
BA_PRECONDITION(have_ba_env_vals_);
return ba_env_app_python_dir_;
}
auto CoreFeatureSet::GetUserPythonDirectory() -> std::optional<std::string> {
BA_PRECONDITION(have_ba_env_vals_);
return ba_env_user_python_dir_;
}
// Return the ballisticakit config dir. This does not vary across versions.
auto CoreFeatureSet::GetConfigDirectory() -> std::string {
BA_PRECONDITION(have_ba_env_vals_);
return ba_env_config_dir_;
}
auto CoreFeatureSet::GetDataDirectory() -> std::string {
BA_PRECONDITION(have_ba_env_vals_);
return ba_env_data_dir_;
}
auto CoreFeatureSet::GetSitePythonDirectory() -> std::optional<std::string> {
BA_PRECONDITION(have_ba_env_vals_);
return ba_env_site_python_dir_;
} }
auto CoreFeatureSet::CalcBuildSrcDir() -> std::string { auto CoreFeatureSet::CalcBuildSrcDir() -> std::string {

View File

@ -3,6 +3,7 @@
#ifndef BALLISTICA_CORE_CORE_H_ #ifndef BALLISTICA_CORE_CORE_H_
#define BALLISTICA_CORE_CORE_H_ #define BALLISTICA_CORE_CORE_H_
#include <list>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <thread> #include <thread>
@ -54,13 +55,15 @@ class CoreFeatureSet {
auto SoftImportBase() -> BaseSoftInterface*; auto SoftImportBase() -> BaseSoftInterface*;
/// The core-config we were inited with. /// The core-config we were inited with.
const auto& core_config() const { return core_config_; } auto core_config() const -> const CoreConfig&;
/// Start a timer to force-kill our process after the set length of time. /// Start a timer to force-kill our process after the set length of time.
/// Can be used during shutdown or when trying to send a crash-report to /// Can be used during shutdown or when trying to send a crash-report to
/// ensure we don't hang indefinitely. /// ensure we don't hang indefinitely.
void StartSuicideTimer(const std::string& action, millisecs_t delay); void StartSuicideTimer(const std::string& action, millisecs_t delay);
void ApplyBaEnvConfig();
// Call this if the main thread changes. // Call this if the main thread changes.
// Fixme: Should come up with something less hacky feeling. // Fixme: Should come up with something less hacky feeling.
void UpdateMainThreadID(); void UpdateMainThreadID();
@ -108,6 +111,38 @@ class CoreFeatureSet {
legacy_user_agent_string_ = val; legacy_user_agent_string_ = val;
} }
/// Return true if baenv values have been locked in: python paths, log
/// handling, etc. Early-running code may wish to explicitly avoid making log
/// calls until this condition is met to ensure predictable behavior.
auto HaveBaEnvVals() const { return have_ba_env_vals_; }
/// Return the directory where the app expects to find its bundled Python
/// files.
auto GetAppPythonDirectory() -> std::optional<std::string>;
/// Return a directory where the local user can manually place Python
/// files where they will be accessible by the app. When possible, this
/// directory should be in a place easily accessible to the user.
auto GetUserPythonDirectory() -> std::optional<std::string>;
/// Get the root config directory. This dir contains the app config file
/// and other data considered essential to the app install. This directory
/// should be included in OS backups.
auto GetConfigDirectory() -> std::string;
/// Get the data directory. This dir contains ba_data and possibly other
/// platform-specific bits needed for the app to function.
auto GetDataDirectory() -> std::string;
/// Return the directory where bundled 3rd party Python files live.
auto GetSitePythonDirectory() -> std::optional<std::string>;
// Are we using a non-standard app python dir (such as a 'sys' dir within a
// user-python-dir).
auto using_custom_app_python_dir() const {
return using_custom_app_python_dir_;
}
// Subsystems. // Subsystems.
CorePython* const python; CorePython* const python;
CorePlatform* const platform; CorePlatform* const platform;
@ -159,6 +194,13 @@ class CoreFeatureSet {
std::mutex app_time_mutex_; std::mutex app_time_mutex_;
std::string legacy_user_agent_string_{ std::string legacy_user_agent_string_{
"BA_USER_AGENT_UNSET (" BA_PLATFORM_STRING ")"}; "BA_USER_AGENT_UNSET (" BA_PLATFORM_STRING ")"};
bool have_ba_env_vals_{};
std::optional<std::string> ba_env_app_python_dir_;
std::string ba_env_config_dir_;
std::optional<std::string> ba_env_user_python_dir_;
std::optional<std::string> ba_env_site_python_dir_;
std::string ba_env_data_dir_;
bool using_custom_app_python_dir_{};
}; };
} // namespace ballistica::core } // namespace ballistica::core

View File

@ -144,7 +144,7 @@ auto CorePlatform::GetLegacyDeviceUUID() -> const std::string& {
// in our config dir. This should be globally-unique, but the downside is // in our config dir. This should be globally-unique, but the downside is
// the user can tamper with it. // the user can tamper with it.
if (!have_real_unique_uuid) { if (!have_real_unique_uuid) {
std::string path = GetConfigDirectory() + BA_DIRSLASH + ".bsuuid"; std::string path = g_core->GetConfigDirectory() + BA_DIRSLASH + ".bsuuid";
if (FILE* f = FOpen(path.c_str(), "rb")) { if (FILE* f = FOpen(path.c_str(), "rb")) {
// There's an existing one; read it. // There's an existing one; read it.
@ -201,13 +201,14 @@ auto CorePlatform::DoGetConfigDirectoryMonolithicDefault()
} }
auto CorePlatform::GetConfigFilePath() -> std::string { auto CorePlatform::GetConfigFilePath() -> std::string {
return GetConfigDirectory() + BA_DIRSLASH + "config.json"; return g_core->GetConfigDirectory() + BA_DIRSLASH + "config.json";
} }
// FIXME: should make this unnecessary. // FIXME: should make this unnecessary.
auto CorePlatform::GetLowLevelConfigValue(const char* key, int default_value) auto CorePlatform::GetLowLevelConfigValue(const char* key, int default_value)
-> int { -> int {
std::string path = GetConfigDirectory() + BA_DIRSLASH + ".cvar_" + key; std::string path =
g_core->GetConfigDirectory() + BA_DIRSLASH + ".cvar_" + key;
int val = default_value; int val = default_value;
FILE* f = FOpen(path.c_str(), "r"); FILE* f = FOpen(path.c_str(), "r");
if (f) { if (f) {
@ -225,7 +226,8 @@ auto CorePlatform::GetLowLevelConfigValue(const char* key, int default_value)
// FIXME: should make this unnecessary. // FIXME: should make this unnecessary.
void CorePlatform::SetLowLevelConfigValue(const char* key, int value) { void CorePlatform::SetLowLevelConfigValue(const char* key, int value) {
std::string path = GetConfigDirectory() + BA_DIRSLASH + ".cvar_" + key; std::string path =
g_core->GetConfigDirectory() + BA_DIRSLASH + ".cvar_" + key;
std::string out = std::to_string(value); std::string out = std::to_string(value);
FILE* f = FOpen(path.c_str(), "w"); FILE* f = FOpen(path.c_str(), "w");
if (f) { if (f) {
@ -238,11 +240,6 @@ void CorePlatform::SetLowLevelConfigValue(const char* key, int value) {
} }
} }
auto CorePlatform::GetUserPythonDirectory() -> std::optional<std::string> {
BA_PRECONDITION(have_ba_env_vals_);
return ba_env_user_python_dir_;
}
auto CorePlatform::GetVolatileDataDirectory() -> std::string { auto CorePlatform::GetVolatileDataDirectory() -> std::string {
if (!made_volatile_data_dir_) { if (!made_volatile_data_dir_) {
volatile_data_dir_ = GetDefaultVolatileDataDirectory(); volatile_data_dir_ = GetDefaultVolatileDataDirectory();
@ -254,23 +251,13 @@ auto CorePlatform::GetVolatileDataDirectory() -> std::string {
auto CorePlatform::GetDefaultVolatileDataDirectory() -> std::string { auto CorePlatform::GetDefaultVolatileDataDirectory() -> std::string {
// By default, stuff this in a subdir under our config dir. // By default, stuff this in a subdir under our config dir.
return GetConfigDirectory() + BA_DIRSLASH + "vdata"; return g_core->GetConfigDirectory() + BA_DIRSLASH + "vdata";
}
auto CorePlatform::GetAppPythonDirectory() -> std::optional<std::string> {
BA_PRECONDITION(have_ba_env_vals_);
return ba_env_app_python_dir_;
}
auto CorePlatform::GetSitePythonDirectory() -> std::optional<std::string> {
BA_PRECONDITION(have_ba_env_vals_);
return ba_env_site_python_dir_;
} }
auto CorePlatform::GetReplaysDir() -> std::string { auto CorePlatform::GetReplaysDir() -> std::string {
static bool made_dir = false; static bool made_dir = false;
if (!made_dir) { if (!made_dir) {
replays_dir_ = GetConfigDirectory() + BA_DIRSLASH + "replays"; replays_dir_ = g_core->GetConfigDirectory() + BA_DIRSLASH + "replays";
MakeDir(replays_dir_); MakeDir(replays_dir_);
made_dir = true; made_dir = true;
} }
@ -355,13 +342,6 @@ auto CorePlatform::GetErrnoString() -> std::string {
#endif #endif
} }
// Return the ballisticakit config dir
// This does not vary across versions.
auto CorePlatform::GetConfigDirectory() -> std::string {
BA_PRECONDITION(have_ba_env_vals_);
return ba_env_config_dir_;
}
auto CorePlatform::GetConfigDirectoryMonolithicDefault() auto CorePlatform::GetConfigDirectoryMonolithicDefault()
-> std::optional<std::string> { -> std::optional<std::string> {
// CoreConfig value trumps all. Otherwise go with platform-specific default. // CoreConfig value trumps all. Otherwise go with platform-specific default.
@ -371,11 +351,6 @@ auto CorePlatform::GetConfigDirectoryMonolithicDefault()
return DoGetConfigDirectoryMonolithicDefault(); return DoGetConfigDirectoryMonolithicDefault();
} }
auto CorePlatform::GetDataDirectory() -> std::string {
BA_PRECONDITION(have_ba_env_vals_);
return ba_env_data_dir_;
}
auto CorePlatform::GetDataDirectoryMonolithicDefault() -> std::string { auto CorePlatform::GetDataDirectoryMonolithicDefault() -> std::string {
// CoreConfig arg trumps all. Otherwise ask for platform-specific value. // CoreConfig arg trumps all. Otherwise ask for platform-specific value.
if (g_core->core_config().data_dir.has_value()) { if (g_core->core_config().data_dir.has_value()) {
@ -1216,32 +1191,4 @@ auto CorePlatform::System(const char* cmd) -> int {
#endif #endif
} }
void CorePlatform::SetBaEnvVals(const PythonRef& ref) {
assert(!have_ba_env_vals_);
have_ba_env_vals_ = true;
ba_env_config_dir_ = ref.GetAttr("config_dir").ValueAsString();
ba_env_data_dir_ = ref.GetAttr("data_dir").ValueAsString();
ba_env_app_python_dir_ =
ref.GetAttr("app_python_dir").ValueAsOptionalString();
ba_env_user_python_dir_ =
ref.GetAttr("user_python_dir").ValueAsOptionalString();
ba_env_site_python_dir_ =
ref.GetAttr("site_python_dir").ValueAsOptionalString();
// Consider app-python-dir 'custom' if baenv provided a value
// for it AND that value differs from baenv's default.
auto standard_app_python_dir =
ref.GetAttr("standard_app_python_dir").ValueAsString();
using_custom_app_python_dir_ =
ba_env_app_python_dir_.has_value()
&& *ba_env_app_python_dir_ != standard_app_python_dir;
// Ok, now look for the existence of ba_data in the dir we've got.
auto fullpath = ba_env_data_dir_ + BA_DIRSLASH + "ba_data";
if (!FilePathExists(fullpath)) {
FatalError("ba_data directory not found at '" + fullpath + "'.");
}
}
} // namespace ballistica::core } // namespace ballistica::core

View File

@ -141,35 +141,16 @@ class CorePlatform {
/// etc). /// etc).
virtual auto GetUIScale() -> UIScale; virtual auto GetUIScale() -> UIScale;
/// Get the data directory. This dir contains ba_data and possibly other
/// platform-specific bits needed for the app to function.
auto GetDataDirectory() -> std::string;
/// Return default DataDirectory value for monolithic builds. /// Return default DataDirectory value for monolithic builds.
auto GetDataDirectoryMonolithicDefault() -> std::string; auto GetDataDirectoryMonolithicDefault() -> std::string;
/// Get the root config directory. This dir contains the app config file
/// and other data considered essential to the app install. This directory
/// should be included in OS backups.
auto GetConfigDirectory() -> std::string;
auto GetConfigDirectoryMonolithicDefault() -> std::optional<std::string>; auto GetConfigDirectoryMonolithicDefault() -> std::optional<std::string>;
/// Get the path of the app config file. /// Get the path of the app config file.
auto GetConfigFilePath() -> std::string; auto GetConfigFilePath() -> std::string;
/// Return a directory where the local user can manually place Python
/// files where they will be accessible by the app. When possible, this
/// directory should be in a place easily accessible to the user.
auto GetUserPythonDirectory() -> std::optional<std::string>;
auto GetUserPythonDirectoryMonolithicDefault() -> std::optional<std::string>; auto GetUserPythonDirectoryMonolithicDefault() -> std::optional<std::string>;
/// Return the directory where the app expects to find its bundled Python
/// files.
auto GetAppPythonDirectory() -> std::optional<std::string>;
/// Return the directory where bundled 3rd party Python files live.
auto GetSitePythonDirectory() -> std::optional<std::string>;
/// Get a directory where the app can store internal generated data. This /// Get a directory where the app can store internal generated data. This
/// directory should not be included in backups and the app should remain /// directory should not be included in backups and the app should remain
/// functional if this directory is completely cleared between runs /// functional if this directory is completely cleared between runs
@ -470,20 +451,9 @@ class CorePlatform {
// return true and set the native full res here. Otherwise return false; // return true and set the native full res here. Otherwise return false;
virtual auto GetDisplayResolution(int* x, int* y) -> bool; virtual auto GetDisplayResolution(int* x, int* y) -> bool;
auto using_custom_app_python_dir() const {
return using_custom_app_python_dir_;
}
/// Are we being run from a terminal? (should we show prompts, etc?). /// Are we being run from a terminal? (should we show prompts, etc?).
auto is_stdin_a_terminal() const { return is_stdin_a_terminal_; } auto is_stdin_a_terminal() const { return is_stdin_a_terminal_; }
void SetBaEnvVals(const PythonRef& ref);
/// Return true if baenv values have been locked in: python paths, log
/// handling, etc. Early-running code may wish to explicitly avoid making log
/// calls until this condition is met to ensure predictable behavior.
auto HaveBaEnvVals() const { return have_ba_env_vals_; }
protected: protected:
/// Are we being run from a terminal? (should we show prompts, etc?). /// Are we being run from a terminal? (should we show prompts, etc?).
virtual auto GetIsStdinATerminal() -> bool; virtual auto GetIsStdinATerminal() -> bool;
@ -544,7 +514,6 @@ class CorePlatform {
private: private:
bool is_stdin_a_terminal_{}; bool is_stdin_a_terminal_{};
bool using_custom_app_python_dir_{};
bool have_has_touchscreen_value_{}; bool have_has_touchscreen_value_{};
bool have_touchscreen_{}; bool have_touchscreen_{};
bool is_tegra_k1_{}; bool is_tegra_k1_{};
@ -553,17 +522,11 @@ class CorePlatform {
bool made_volatile_data_dir_{}; bool made_volatile_data_dir_{};
bool have_device_uuid_{}; bool have_device_uuid_{};
bool ran_base_post_init_{}; bool ran_base_post_init_{};
bool have_ba_env_vals_{};
millisecs_t start_time_millisecs_{}; millisecs_t start_time_millisecs_{};
std::string device_name_; std::string device_name_;
std::string legacy_device_uuid_; std::string legacy_device_uuid_;
std::string volatile_data_dir_; std::string volatile_data_dir_;
std::string replays_dir_; std::string replays_dir_;
std::string ba_env_config_dir_;
std::string ba_env_data_dir_;
std::optional<std::string> ba_env_app_python_dir_;
std::optional<std::string> ba_env_user_python_dir_;
std::optional<std::string> ba_env_site_python_dir_;
}; };
} // namespace ballistica::core } // namespace ballistica::core

View File

@ -15,13 +15,6 @@ void LowLevelPythonDebugLog(const char* msg) {
g_core->platform->DebugLog(msg); 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();
BA_PRECONDITION(envcfg.Exists());
g_core->platform->SetBaEnvVals(envcfg);
}
static void CheckPyInitStatus(const char* where, const PyStatus& status) { static void CheckPyInitStatus(const char* where, const PyStatus& status) {
if (PyStatus_Exception(status)) { if (PyStatus_Exception(status)) {
FatalError(std::string("Error in ") + where + ": " FatalError(std::string("Error in ") + where + ": "
@ -337,4 +330,76 @@ void CorePython::LoggingCall(LogLevel loglevel, const std::string& msg) {
objs().Get(logcallobj).Call(args); objs().Get(logcallobj).Call(args);
} }
auto CorePython::WasModularMainCalled() -> bool {
assert(!g_buildconfig.monolithic_build());
// This gets called in modular builds before anything is inited, so we need to
// avoid using anything from g_core or whatnot here; only raw Python stuff.
PyObject* baenv = PyImport_ImportModule("baenv");
if (!baenv) {
FatalError("Unable to import baenv module.");
}
PyObject* env_globals_class = PyObject_GetAttrString(baenv, "_EnvGlobals");
if (!env_globals_class) {
FatalError("_EnvGlobals class not found in baenv.");
}
PyObject* get_call = PyObject_GetAttrString(env_globals_class, "get");
if (!get_call) {
FatalError("get() call not found on baenv._EnvGlobals.");
}
PyObject* env_globals_instance = PyObject_CallNoArgs(get_call);
if (!get_call) {
FatalError("baenv._EnvGlobals.get() call failed.");
}
PyObject* modular_main_called =
PyObject_GetAttrString(env_globals_instance, "modular_main_called");
if (!modular_main_called || !PyBool_Check(modular_main_called)) {
FatalError("modular_main_called bool not found on baenv _EnvGlobals.");
}
assert(modular_main_called == Py_True || modular_main_called == Py_False);
bool val = modular_main_called == Py_True;
Py_DECREF(baenv);
Py_DECREF(env_globals_class);
Py_DECREF(get_call);
Py_DECREF(env_globals_instance);
Py_DECREF(modular_main_called);
return val;
}
auto CorePython::FetchPythonArgs(std::vector<std::string>* buffer)
-> std::vector<char*> {
// This gets called in modular builds before anything is inited, so we need to
// avoid using anything from g_core or whatnot here; only raw Python stuff.
assert(buffer && buffer->empty());
PyObject* sys = PyImport_ImportModule("sys");
if (!sys) {
FatalError("Unable to import sys module.");
}
PyObject* argv = PyObject_GetAttrString(sys, "argv");
if (!argv || !PyList_Check(argv)) {
FatalError("Unable to fetch sys.argv list.");
}
Py_ssize_t listlen = PyList_GET_SIZE(argv);
for (Py_ssize_t i = 0; i < listlen; ++i) {
PyObject* arg = PyList_GET_ITEM(argv, i);
BA_PRECONDITION_FATAL(PyUnicode_Check(arg));
buffer->push_back(PyUnicode_AsUTF8(arg));
}
Py_DECREF(sys);
Py_DECREF(argv);
// Ok, we've filled the buffer so it won't be resizing anymore. Now set up
// argv pointers to it.
std::vector<char*> out;
out.reserve(buffer->size());
for (int i = 0; i < buffer->size(); ++i) {
out.push_back(const_cast<char*>((*buffer)[i].c_str()));
}
return out;
}
} // namespace ballistica::core } // namespace ballistica::core

View File

@ -43,10 +43,6 @@ class CorePython {
/// pent up ones) to Python. /// pent up ones) to Python.
void EnablePythonLoggingCalls(); void EnablePythonLoggingCalls();
/// Should be called just before base feature set import; locks in the
/// baenv environment and runs some checks.
void ApplyBaEnvConfig();
/// Calls Python logging function (logging.error, logging.warning, etc.) /// Calls Python logging function (logging.error, logging.warning, etc.)
/// Can be called from any thread at any time. If called before Python /// Can be called from any thread at any time. If called before Python
/// logging is available, logs locally using Logging::DisplayLog() /// logging is available, logs locally using Logging::DisplayLog()
@ -56,6 +52,13 @@ class CorePython {
void VerifyPythonEnvironment(); void VerifyPythonEnvironment();
void SoftImportBase(); void SoftImportBase();
static auto WasModularMainCalled() -> bool;
/// Builds a vector of strings out of Python's sys.argv. Returns an argv
/// array pointing to them.
static auto FetchPythonArgs(std::vector<std::string>* buffer)
-> std::vector<char*>;
const auto& objs() { return objs_; } const auto& objs() { return objs_; }
private: private:

View File

@ -75,45 +75,36 @@ static auto ParseArgValue(int argc, char** argv, int* i, const char* arg_long,
return {}; return {};
} }
auto CoreConfig::FromCommandLineAndEnv(int argc, char** argv) -> CoreConfig { void CoreConfig::ApplyEnvVars() {
auto cfg = CoreConfig();
// First set any values we allow env-vars for.
// We want explicitly passed values to override these in any cases where both
// forms are accepted.
if (auto* envval = getenv("BA_LIFECYCLE_LOG")) { if (auto* envval = getenv("BA_LIFECYCLE_LOG")) {
if (!strcmp(envval, "1")) { if (!strcmp(envval, "1")) {
cfg.lifecycle_log = true; lifecycle_log = true;
} }
} }
if (auto* envval = getenv("BA_DEBUGGER_ATTACHED")) { if (auto* envval = getenv("BA_DEBUGGER_ATTACHED")) {
if (!strcmp(envval, "1")) { if (!strcmp(envval, "1")) {
cfg.debugger_attached = true; debugger_attached = true;
} }
} }
if (auto* envval = getenv("BA_DEBUG_TIMING")) { if (auto* envval = getenv("BA_DEBUG_TIMING")) {
if (!strcmp(envval, "1")) { if (!strcmp(envval, "1")) {
cfg.debug_timing = true; debug_timing = true;
} }
} }
}
// REMOVE ME FOR 1.7.20 FINAL. void CoreConfig::ApplyArgs(int argc, char** argv) {
if (explicit_bool(false)) {
printf("TEMP: forcing BA_LIFECYCLE_LOG=1 during 1.7.20 development.\n");
cfg.lifecycle_log = true;
}
try { try {
// First handle single-arg special cases like --help or --version. // First handle single-arg special cases like --help or --version.
if (IsSingleArgSpecialCase(argc, argv, "--help", "-h")) { if (IsSingleArgSpecialCase(argc, argv, "--help", "-h")) {
PrintHelp(); PrintHelp();
cfg.immediate_return_code = 0; immediate_return_code = 0;
return cfg; return;
} }
if (IsSingleArgSpecialCase(argc, argv, "--version", "-v")) { if (IsSingleArgSpecialCase(argc, argv, "--version", "-v")) {
printf("BallisticaKit %s build %d\n", kEngineVersion, kEngineBuildNumber); printf("BallisticaKit %s build %d\n", kEngineVersion, kEngineBuildNumber);
cfg.immediate_return_code = 0; immediate_return_code = 0;
return cfg; return;
} }
if (IsSingleArgSpecialCase(argc, argv, "--crash")) { if (IsSingleArgSpecialCase(argc, argv, "--crash")) {
int dummyval{}; int dummyval{};
@ -126,7 +117,7 @@ auto CoreConfig::FromCommandLineAndEnv(int argc, char** argv) -> CoreConfig {
if (explicit_bool(true)) { if (explicit_bool(true)) {
*invalid_ptr = 1; *invalid_ptr = 1;
} }
return cfg; return;
} }
// Ok, all single-arg cases handled; now go through everything else // Ok, all single-arg cases handled; now go through everything else
@ -135,35 +126,35 @@ auto CoreConfig::FromCommandLineAndEnv(int argc, char** argv) -> CoreConfig {
std::optional<std::string> value; std::optional<std::string> value;
while (i < argc) { while (i < argc) {
if ((value = ParseArgValue(argc, argv, &i, "--command", "-c"))) { if ((value = ParseArgValue(argc, argv, &i, "--command", "-c"))) {
cfg.call_command = *value; call_command = *value;
} else if ((value = ParseArgValue(argc, argv, &i, "--exec", "-e"))) { } else if ((value = ParseArgValue(argc, argv, &i, "--exec", "-e"))) {
cfg.exec_command = *value; exec_command = *value;
} else if ((value = } else if ((value =
ParseArgValue(argc, argv, &i, "--config-dir", "-C"))) { ParseArgValue(argc, argv, &i, "--config-dir", "-C"))) {
cfg.config_dir = *value; config_dir = *value;
// Make sure what they passed exists. // Make sure what they passed exists.
// Note: Normally baenv will try to create whatever the config dir is; // Note: Normally baenv will try to create whatever the config dir is;
// do we just want to allow that to happen in this case? But perhaps // do we just want to allow that to happen in this case? But perhaps
// being more strict is ok when accepting user input. // being more strict is ok when accepting user input.
if (!std::filesystem::exists(*cfg.config_dir)) { if (!std::filesystem::is_directory(*config_dir)) {
printf("Error: Provided config dir does not exist: '%s'.", printf("Error: Provided config-dir path '%s' is not a directory.",
cfg.config_dir->c_str()); config_dir->c_str());
throw BadArgsException(); throw BadArgsException();
} }
} else if ((value = ParseArgValue(argc, argv, &i, "--data-dir", "-d"))) { } else if ((value = ParseArgValue(argc, argv, &i, "--data-dir", "-d"))) {
cfg.data_dir = *value; data_dir = *value;
// Make sure what they passed exists. // Make sure what they passed exists.
if (!std::filesystem::exists(*cfg.data_dir)) { if (!std::filesystem::is_directory(*data_dir)) {
printf("Error: Provided data dir does not exist: '%s'.", printf("Error: Provided data-dir path '%s' is not a directory.",
cfg.data_dir->c_str()); data_dir->c_str());
throw BadArgsException(); throw BadArgsException();
} }
} else if ((value = ParseArgValue(argc, argv, &i, "--mods-dir", "-m"))) { } else if ((value = ParseArgValue(argc, argv, &i, "--mods-dir", "-m"))) {
cfg.user_python_dir = *value; user_python_dir = *value;
// Make sure what they passed exists. // Make sure what they passed exists.
if (!std::filesystem::exists(*cfg.user_python_dir)) { if (!std::filesystem::is_directory(*user_python_dir)) {
printf("Error: Provided mods dir does not exist: '%s'.", printf("Error: Provided mods-dir path '%s' is not a directory.",
cfg.user_python_dir->c_str()); user_python_dir->c_str());
throw BadArgsException(); throw BadArgsException();
} }
} else { } else {
@ -175,8 +166,25 @@ auto CoreConfig::FromCommandLineAndEnv(int argc, char** argv) -> CoreConfig {
} }
} }
} catch (const BadArgsException&) { } catch (const BadArgsException&) {
cfg.immediate_return_code = 1; immediate_return_code = 1;
} }
}
auto CoreConfig::ForEnvVars() -> CoreConfig {
CoreConfig cfg{};
cfg.ApplyEnvVars();
return cfg;
}
auto CoreConfig::ForArgsAndEnvVars(int argc, char** argv) -> CoreConfig {
CoreConfig cfg{};
// Apply env-vars first. We want explicit args to override these.
cfg.ApplyEnvVars();
cfg.ApplyArgs(argc, argv);
return cfg; return cfg;
} }

View File

@ -14,7 +14,17 @@ namespace ballistica::core {
/// when initing the core feature-set. /// when initing the core feature-set.
class CoreConfig { class CoreConfig {
public: public:
static auto FromCommandLineAndEnv(int argc, char** argv) -> CoreConfig; static auto ForArgsAndEnvVars(int argc, char** argv) -> CoreConfig;
static auto ForEnvVars() -> CoreConfig;
/// Build a core-config for a modular app being run from the command-line.
/// In this case, Python has already been inited and Ballistica has
/// already been imported (since that's where this code lives) so there is
/// less that can be affected by a core-config.
void ApplyEnvVars();
void ApplyArgs(int argc, char** argv);
/// Enable vr mode on supported platforms. /// Enable vr mode on supported platforms.
bool vr_mode{}; bool vr_mode{};
@ -26,19 +36,19 @@ class CoreConfig {
std::optional<int> immediate_return_code{}; std::optional<int> immediate_return_code{};
/// If set, this single Python command will be run instead of the /// If set, this single Python command will be run instead of the
/// normal app loop. /// normal app loop (monolithic builds only).
std::optional<std::string> call_command{}; std::optional<std::string> call_command{};
/// Python command to be run within the normal app loop. /// Python command to be run within the normal app loop.
std::optional<std::string> exec_command{}; std::optional<std::string> exec_command{};
/// Explicitly set config dir. /// Explicitly passed config dir.
std::optional<std::string> config_dir{}; std::optional<std::string> config_dir{};
/// Explicitly set data dir. /// Explicitly passed data dir.
std::optional<std::string> data_dir{}; std::optional<std::string> data_dir{};
/// Explicitly set user-python (mods) dir. /// Explicitly passed user-python (mods) dir.
std::optional<std::string> user_python_dir{}; std::optional<std::string> user_python_dir{};
/// Log various stages/times in the bootstrapping process. /// Log various stages/times in the bootstrapping process.

View File

@ -25,7 +25,7 @@
#if BA_MONOLITHIC_BUILD && BA_DEFINE_MAIN #if BA_MONOLITHIC_BUILD && BA_DEFINE_MAIN
auto main(int argc, char** argv) -> int { auto main(int argc, char** argv) -> int {
auto core_config = auto core_config =
ballistica::core::CoreConfig::FromCommandLineAndEnv(argc, argv); ballistica::core::CoreConfig::ForArgsAndEnvVars(argc, argv);
// Arg-parsing may have yielded an error or printed simple output for // Arg-parsing may have yielded an error or printed simple output for
// things such as '--help', in which case we're done. // things such as '--help', in which case we're done.
@ -39,7 +39,7 @@ auto main(int argc, char** argv) -> int {
namespace ballistica { namespace ballistica {
// These are set automatically via script; don't modify them here. // These are set automatically via script; don't modify them here.
const int kEngineBuildNumber = 21196; const int kEngineBuildNumber = 21199;
const char* kEngineVersion = "1.7.24"; const char* kEngineVersion = "1.7.24";
#if BA_MONOLITHIC_BUILD #if BA_MONOLITHIC_BUILD
@ -65,9 +65,6 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int {
// import it first thing even if we don't explicitly use it. // import it first thing even if we don't explicitly use it.
l_core = core::CoreFeatureSet::Import(&core_config); 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 // 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 // simply as a Python interpreter in that case; we don't do any
// environment setup (aside from the bits core does automatically such // environment setup (aside from the bits core does automatically such
@ -80,9 +77,6 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int {
exit(success ? 0 : 1); exit(success ? 0 : 1);
} }
// TEMP - bug hunting.
l_core->platform->DebugLog("mm2");
// Ok, looks like we're doing a standard monolithic-mode app run. // Ok, looks like we're doing a standard monolithic-mode app run.
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
@ -95,9 +89,6 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int {
// those modules get loaded from in the first place. // those modules get loaded from in the first place.
l_core->python->MonolithicModeBaEnvConfigure(); 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 // 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. // dependency to it. Let's see if it's available.
l_base = l_core->SoftImportBase(); l_base = l_core->SoftImportBase();
@ -109,9 +100,6 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int {
// Phase 2: "The pieces are moving." // 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 // Spin up all app machinery such as threads and subsystems. This gets
// things ready to rock, but there's no actual rocking quite yet. // things ready to rock, but there's no actual rocking quite yet.
l_base->StartApp(); l_base->StartApp();
@ -120,9 +108,6 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int {
// Phase 3: "We come to it at last; the great battle of our time." // 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 // 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 // until the app exits (or we return from this function and let the
// environment do that part). // environment do that part).
@ -165,7 +150,6 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int {
} }
} }
} }
if (l_core) { if (l_core) {
l_core->platform->WillExitMain(false); l_core->platform->WillExitMain(false);
return l_core->return_value; return l_core->return_value;

View File

@ -105,7 +105,8 @@ auto Python::GetPyString(PyObject* o) -> std::string {
return PyUnicode_AsUTF8(o); return PyUnicode_AsUTF8(o);
} }
throw Exception( throw Exception(
"Can't get string from value: " + Python::ObjToString(o) + ".", exctype); "Expected a string object; got type " + Python::ObjTypeToString(o) + ".",
exctype);
} }
template <typename T> template <typename T>
@ -220,6 +221,24 @@ auto Python::GetPyFloats(PyObject* o) -> std::vector<float> {
return vals; return vals;
} }
auto Python::GetPyStrings(PyObject* o) -> std::list<std::string> {
assert(HaveGIL());
BA_PRECONDITION_FATAL(o != nullptr);
if (!PySequence_Check(o)) {
throw Exception("Object is not a sequence.", PyExcType::kType);
}
PythonRef sequence(PySequence_Fast(o, "Not a sequence."), PythonRef::kSteal);
assert(sequence.Exists());
Py_ssize_t size = PySequence_Fast_GET_SIZE(sequence.Get());
PyObject** py_objects = PySequence_Fast_ITEMS(sequence.Get());
std::list<std::string> vals;
for (Py_ssize_t i = 0; i < size; i++) {
vals.emplace_back(Python::GetPyString(py_objects[i]));
}
return vals;
}
template <typename T> template <typename T>
auto GetPyIntsT(PyObject* o) -> std::vector<T> { auto GetPyIntsT(PyObject* o) -> std::vector<T> {
assert(Python::HaveGIL()); assert(Python::HaveGIL());

View File

@ -121,6 +121,7 @@ class Python {
static auto GetPyInts(PyObject* o) -> std::vector<int>; static auto GetPyInts(PyObject* o) -> std::vector<int>;
static auto GetPyUInts64(PyObject* o) -> std::vector<uint64_t>; static auto GetPyUInts64(PyObject* o) -> std::vector<uint64_t>;
static auto GetPyPoint2D(PyObject* o) -> Point2D; static auto GetPyPoint2D(PyObject* o) -> Point2D;
static auto GetPyStrings(PyObject* o) -> std::list<std::string>;
/// Set Python exception from C++ Exception. /// Set Python exception from C++ Exception.
static void SetPythonException(const Exception& exc); static void SetPythonException(const Exception& exc);

View File

@ -12,14 +12,14 @@ namespace ballistica {
// String based Python commands. // String based Python commands.
// Note to self: originally I though I'd be using this in a lot of places, // Note to self: Originally I though I'd be using this in a lot of places,
// so I added the ability to compile once and run repeatedly, quietly capture // so I added the ability to compile once and run repeatedly, quietly
// output instead of printing it, etc. Now, however, its usage is pretty // capture output instead of printing it, etc. Now, however, its usage is
// much limited to a few places such as handling stdin and the in-game console. // pretty much limited to a few places such as handling stdin and the
// (Most places it is much cleaner to work with proper python modules and just // in-app console. (Most places it is much cleaner to work with proper
// interact with PyObject* refs to them) // python modules and just interact with PyObject* refs to them) I should
// I should look and see if python's default high level calls would suffice // look and see if python's default high level calls would suffice for these
// for these purposes and potentially kill this off. // purposes and potentially kill this off.
class PythonCommand { class PythonCommand {
public: public:
PythonCommand(); PythonCommand();

View File

@ -4,6 +4,7 @@
#include "ballistica/core/python/core_python.h" #include "ballistica/core/python/core_python.h"
#include "ballistica/core/support/base_soft.h" #include "ballistica/core/support/base_soft.h"
#include "ballistica/shared/foundation/types.h"
#include "ballistica/shared/math/vector2f.h" #include "ballistica/shared/math/vector2f.h"
#include "ballistica/shared/python/python.h" #include "ballistica/shared/python/python.h"
#include "ballistica/shared/python/python_sys.h" #include "ballistica/shared/python/python_sys.h"
@ -13,7 +14,6 @@ namespace ballistica {
// Note: implicitly using core globals here; our behavior is undefined // Note: implicitly using core globals here; our behavior is undefined
// if core has not been imported by anyone yet. // if core has not been imported by anyone yet.
using core::g_base_soft; using core::g_base_soft;
using core::g_core;
// Ignore a few things that python macros do. // Ignore a few things that python macros do.
#pragma clang diagnostic push #pragma clang diagnostic push
@ -179,6 +179,16 @@ auto PythonRef::ValueAsOptionalString() const -> std::optional<std::string> {
return Python::GetPyString(obj_); return Python::GetPyString(obj_);
} }
auto PythonRef::ValueAsOptionalStringSequence() const
-> std::optional<std::list<std::string>> {
assert(Python::HaveGIL());
ThrowIfUnset();
if (obj_ == Py_None) {
return {};
}
return Python::GetPyStrings(obj_);
}
auto PythonRef::ValueAsInt() const -> int64_t { auto PythonRef::ValueAsInt() const -> int64_t {
assert(Python::HaveGIL()); assert(Python::HaveGIL());
ThrowIfUnset(); ThrowIfUnset();
@ -238,15 +248,8 @@ auto PythonRef::UnicodeCheck() const -> bool {
return static_cast<bool>(PyUnicode_Check(obj_)); return static_cast<bool>(PyUnicode_Check(obj_));
} }
auto PythonRef::Call(PyObject* args, PyObject* keywds, bool print_errors) const static inline auto _HandleCallResults(PyObject* out, bool print_errors)
-> PythonRef { -> PyObject* {
assert(obj_);
assert(Python::HaveGIL());
assert(CallableCheck());
assert(args);
assert(PyTuple_Check(args)); // NOLINT (signed bitwise stuff)
assert(!keywds || PyDict_Check(keywds)); // NOLINT (signed bitwise)
PyObject* out = PyObject_Call(obj_, args, keywds);
if (!out) { if (!out) {
if (print_errors) { if (print_errors) {
// Save/restore error or it can mess with context print calls. // Save/restore error or it can mess with context print calls.
@ -262,20 +265,36 @@ auto PythonRef::Call(PyObject* args, PyObject* keywds, bool print_errors) const
} }
PyErr_Clear(); PyErr_Clear();
} }
return out;
}
auto PythonRef::Call(PyObject* args, PyObject* keywds, bool print_errors) const
-> PythonRef {
assert(obj_);
assert(Python::HaveGIL());
assert(CallableCheck());
assert(args);
assert(PyTuple_Check(args)); // NOLINT (signed bitwise stuff)
assert(!keywds || PyDict_Check(keywds)); // NOLINT (signed bitwise)
PyObject* out = PyObject_Call(obj_, args, keywds);
out = _HandleCallResults(out, print_errors);
return out ? PythonRef(out, PythonRef::kSteal) : PythonRef(); return out ? PythonRef(out, PythonRef::kSteal) : PythonRef();
} }
auto PythonRef::Call() const -> PythonRef { auto PythonRef::Call(bool print_errors) const -> PythonRef {
// NOTE: Using core globals directly here; normally don't do this. assert(obj_);
assert(g_core); assert(Python::HaveGIL());
return Call( assert(CallableCheck());
g_core->python->objs().Get(core::CorePython::ObjID::kEmptyTuple).Get()); PyObject* out = PyObject_CallNoArgs(obj_);
out = _HandleCallResults(out, print_errors);
return out ? PythonRef(out, PythonRef::kSteal) : PythonRef();
} }
auto PythonRef::Call(const Vector2f& val) const -> PythonRef { auto PythonRef::Call(const Vector2f& val, bool print_errors) const
-> PythonRef {
assert(Python::HaveGIL()); assert(Python::HaveGIL());
PythonRef args(Py_BuildValue("((ff))", val.x, val.y), PythonRef::kSteal); PythonRef args(Py_BuildValue("((ff))", val.x, val.y), PythonRef::kSteal);
return Call(args); return Call(args.Get(), nullptr, print_errors);
} }
PythonRef::~PythonRef() { Release(); } PythonRef::~PythonRef() { Release(); }

View File

@ -3,6 +3,7 @@
#ifndef BALLISTICA_SHARED_PYTHON_PYTHON_REF_H_ #ifndef BALLISTICA_SHARED_PYTHON_PYTHON_REF_H_
#define BALLISTICA_SHARED_PYTHON_PYTHON_REF_H_ #define BALLISTICA_SHARED_PYTHON_PYTHON_REF_H_
#include <list>
#include <optional> #include <optional>
#include <string> #include <string>
@ -166,6 +167,8 @@ class PythonRef {
auto ValueAsString() const -> std::string; auto ValueAsString() const -> std::string;
auto ValueAsOptionalString() const -> std::optional<std::string>; auto ValueAsOptionalString() const -> std::optional<std::string>;
auto ValueAsOptionalStringSequence() const
-> std::optional<std::list<std::string>>;
auto ValueAsInt() const -> int64_t; auto ValueAsInt() const -> int64_t;
@ -185,10 +188,10 @@ class PythonRef {
bool print_errors = true) const -> PythonRef { bool print_errors = true) const -> PythonRef {
return Call(args.Get(), keywds.Get(), print_errors); return Call(args.Get(), keywds.Get(), print_errors);
} }
auto Call() const -> PythonRef; auto Call(bool print_errors = true) const -> PythonRef;
/// Call with Vector2f passed as a tuple. /// Call with Vector2f passed as a tuple.
auto Call(const Vector2f& val) const -> PythonRef; auto Call(const Vector2f& val, bool print_errors = true) const -> PythonRef;
private: private:
void ThrowIfUnset() const; void ThrowIfUnset() const;

View File

@ -271,6 +271,8 @@ def _writefuncs(
returnstr = 'return (0.0, 0.0)' returnstr = 'return (0.0, 0.0)'
elif returns == 'str | None': elif returns == 'str | None':
returnstr = "return ''" returnstr = "return ''"
elif returns == 'int | None':
returnstr = 'return 0'
elif returns == 'tuple[float, float, float, float]': elif returns == 'tuple[float, float, float, float]':
returnstr = 'return (0.0, 0.0, 0.0, 0.0)' returnstr = 'return (0.0, 0.0, 0.0, 0.0)'
elif returns == 'bauiv1.Widget | None': elif returns == 'bauiv1.Widget | None':

View File

@ -105,7 +105,7 @@ class Pruner:
self.paths = [os.path.abspath(p) for p in self.paths] self.paths = [os.path.abspath(p) for p in self.paths]
def _get_entries(self) -> list[_CompileCommandsEntry]: def _get_entries(self) -> list[_CompileCommandsEntry]:
cmdspath = '.cache/irony/compile_commands.json' cmdspath = '.cache/compile_commands_db/compile_commands.json'
if not os.path.isfile(cmdspath): if not os.path.isfile(cmdspath):
raise CleanError( raise CleanError(
f'Compile-commands not found at "{cmdspath}".' f'Compile-commands not found at "{cmdspath}".'

View File

@ -786,34 +786,16 @@ class SpinoffContext:
if 'base' in self._src_omit_feature_sets: if 'base' in self._src_omit_feature_sets:
text = replace_exact( text = replace_exact(
text, text,
( ' import babase\n',
'def _main() -> None:\n' ' # (Hack; spinoff disabled babase).\n'
' # Run a default configure BEFORE importing' ' if TYPE_CHECKING:\n'
' babase.\n' ' from typing import Any\n'
' # (may affect where babase comes from).\n' '\n'
' configure()\n' ' # import babase\n'
'\n' '\n'
' import babase\n' ' babase: Any = None\n'
'\n' ' if bool(True):\n'
' babase.app.run()\n' " raise CleanError('babase not present')\n",
'\n'
),
(
'def _main() -> None:\n'
' # DISABLED; REQUIRES BASE FEATURE SET.\n'
' # Run a default configure BEFORE importing'
' babase.\n'
' # (may affect where babase comes from).\n'
' # configure()\n'
'\n'
' # import babase\n'
'\n'
' # babase.app.run()\n'
'\n'
" raise RuntimeError('App-exec requires"
" base feature set.')\n"
'\n'
),
label=src_path, label=src_path,
) )
@ -1587,7 +1569,7 @@ class SpinoffContext:
os.chmod(dst_path_full, mode) os.chmod(dst_path_full, mode)
else: else:
raise RuntimeError( raise RuntimeError(
f"Invalid entity type: '{src_entity['type']}'." f"Invalid entity type: '{src_entity.entity_type}'."
) )
# NOTE TO SELF - was using lchmod here but it doesn't exist # NOTE TO SELF - was using lchmod here but it doesn't exist

View File

@ -526,7 +526,7 @@ class AssetStager:
'# Basically this will do:\n' '# Basically this will do:\n'
'# import baenv; baenv.configure();' '# import baenv; baenv.configure();'
' import babase; babase.app.run().\n' ' import babase; babase.app.run().\n'
'exec python3.11 ba_data/python/baenv.py $@\n' 'exec python3.11 ba_data/python/baenv.py "$@"\n'
) )
subprocess.run(['chmod', '+x', path], check=True) subprocess.run(['chmod', '+x', path], check=True)

View File

@ -25,16 +25,18 @@ class CleanError(Exception):
more descriptive exception types. more descriptive exception types.
""" """
def pretty_print(self, flush: bool = False) -> None: def pretty_print(self, flush: bool = True, prefix: str = 'Error') -> None:
"""Print the error to stdout, using red colored output if available. """Print the error to stdout, using red colored output if available.
If the error has an empty message, prints nothing (not even a newline). If the error has an empty message, prints nothing (not even a newline).
""" """
from efro.terminal import Clr from efro.terminal import Clr
if prefix:
prefix = f'{prefix}: '
errstr = str(self) errstr = str(self)
if errstr: if errstr:
print(f'{Clr.SRED}{errstr}{Clr.RST}', flush=flush) print(f'{Clr.SRED}{prefix}{errstr}{Clr.RST}', flush=flush)
class CommunicationError(Exception): class CommunicationError(Exception):