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/zoePickup01.ogg": "https://files.ballistica.net/cache/ba1/48/ab/8cddfcde36a750856f3f81dd20c8",
"build/assets/ba_data/audio/zoeScream01.ogg": "https://files.ballistica.net/cache/ba1/2b/46/8aedfa8741090247f04eb9e6df55",
"build/assets/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/b8/5e/c8766634397fb77ae3a407c05d63",
"build/assets/ba_data/data/languages/arabic.json": "https://files.ballistica.net/cache/ba1/6f/38/958616d8cb85916aa8b2bcd84f63",
"build/assets/ba_data/data/languages/belarussian.json": "https://files.ballistica.net/cache/ba1/57/68/d03a19b9035cfae7cdc5377d889a",
"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/db/96/1f7fe0541a31880929e1c17ea957",
"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/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",
@ -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/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/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/german.json": "https://files.ballistica.net/cache/ba1/54/97/54d2a530d825200c6126be56df5c",
"build/assets/ba_data/data/languages/gibberish.json": "https://files.ballistica.net/cache/ba1/23/6f/8547ba09722b7c7f5b8333986984",
"build/assets/ba_data/data/languages/greek.json": "https://files.ballistica.net/cache/ba1/a6/5d/78f912e9a89f98de004405167a6a",
"build/assets/ba_data/data/languages/hindi.json": "https://files.ballistica.net/cache/ba1/88/ee/0cda537bab9ac827def5e236fe1a",
"build/assets/ba_data/data/languages/hungarian.json": "https://files.ballistica.net/cache/ba1/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/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/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/portuguese.json": "https://files.ballistica.net/cache/ba1/99/b2/7c598c90fd522132af3536aef0ee",
"build/assets/ba_data/data/languages/romanian.json": "https://files.ballistica.net/cache/ba1/ae/eb/dd54f65939c2facc6ac50c117826",
"build/assets/ba_data/data/languages/russian.json": "https://files.ballistica.net/cache/ba1/aa/99/f9f597787fe4e09c8ab53fe2e081",
"build/assets/ba_data/data/languages/serbian.json": "https://files.ballistica.net/cache/ba1/d7/45/2dd72ac0e51680cb39b5ebaa1c69",
"build/assets/ba_data/data/languages/slovak.json": "https://files.ballistica.net/cache/ba1/27/96/2d53dc3f7dd4e877cd40faafeeef",
"build/assets/ba_data/data/languages/spanish.json": "https://files.ballistica.net/cache/ba1/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/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/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/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",
@ -4068,50 +4068,50 @@
"build/assets/windows/Win32/ucrtbased.dll": "https://files.ballistica.net/cache/ba1/2d/ef/5335207d41b21b9823f6805997f1",
"build/assets/windows/Win32/vc_redist.x86.exe": "https://files.ballistica.net/cache/ba1/b0/8a/55e2e77623fe657bea24f223a3ae",
"build/assets/windows/Win32/vcruntime140d.dll": "https://files.ballistica.net/cache/ba1/86/5b/2af4d1e26a1a8073c89acb06e599",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/6b/a1/5f560a97ab8641091343c6ee688b",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/50/dd/86cbb96aca3a339318b00574b2db",
"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/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/93/04/19410cb96b5c12fc2cd20dd9c099",
"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/release/ballisticakit": "https://files.ballistica.net/cache/ba1/16/68/6011835e4db7927b26761847950b",
"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/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/8a/76/54da9b7ff4d79164d3f4dea2782b",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/4a/08/bf75de3244efe6fc342139a6da32",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/33/c7/d3534c1d605b5bcc4a541457cde9",
"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/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/f5/be/f80777972954ebe6fd91b52a6533",
"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/release/ballisticakit": "https://files.ballistica.net/cache/ba1/23/46/72f453ea380bd5f04957886c2b57",
"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/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/b5/e6/451f3cc73b5b79d21b19c2416d61",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/b2/0e/778420dfd1a6f81ed457a94f8f1d",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/00/76/0f4dd3bbf7a98f00221307535b4f",
"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/release/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/82/99/e0a873f37e95674f2151ea99bf3d",
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/b3/a2/5da0c4dc65f469e4a476e0395eb5",
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/a9/9a/adb83188f9c7d7b51dafd0f8b8a8",
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/b3/a2/5da0c4dc65f469e4a476e0395eb5",
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/a9/9a/adb83188f9c7d7b51dafd0f8b8a8",
"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/release/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/7b/f3/f98278c9654a972baf65d5f04c12",
"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/release/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/7b/f3/f98278c9654a972baf65d5f04c12",
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/c7/a2/40728a3ebfb3006c7a47b698214f",
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/84/19/a1bbbf42c50329f0cd1377d103bb",
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/c7/a2/40728a3ebfb3006c7a47b698214f",
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/84/19/a1bbbf42c50329f0cd1377d103bb",
"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/release/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/59/fe/a3e369f2db87a305641e74ae70ab",
"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/release/libballisticaplus.a": "https://files.ballistica.net/cache/ba1/59/fe/a3e369f2db87a305641e74ae70ab",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/91/2f/362a643d543963de549d830fe604",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/2e/d4/67d6c0f9b372eb5cd92c9def6fc8",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/68/8b/d6049425f1069d256abdaf90004c",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/3e/82/f70e75696765ac05875cb5dd778c",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/38/ee/56658557aa2ecabd0d30eb01b68e",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/6b/4c/568766d02bbca174752488850737",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/3e/5b/1aa2252706188de69075eb7b3656",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/ab/a7/21bc5acc8a823bd7c7ec940e97bc",
"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/3f/fe/4c9f0926b60c6a4f5253fde8bb35",
"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/37/ba/ee706254c5a21f08b8d4c47bb6e2",
"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/11/f3/0feed20f5d34c23d96b39e5702ae",
"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/bc/b1/66680fead22bdecfcd4c8d825f8b",
"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/ef/1c/ddcdbedb47affd72d2f4c5164fd9",
"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/c1/ed/4691901b692a59e598bd982f6d5b",
"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/fa/8c/79e39ee8297c9946f3dfed5d47c4",
"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/2a/c3/4378d0290b122a345787b46ef23c",
"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/c2/4b/04f9d9a58fa1d1cefc096cf84e57",
"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/65/8f/25f37b8f6175fbe9274576e2eb4a",
"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/9d/2b/44367bfb114e7b1148e4e100acd1",
"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/9d/2b/44367bfb114e7b1148e4e100acd1",
"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/a3/38/2c4abbc88434720311048185fdd1",
"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/a3/38/2c4abbc88434720311048185fdd1",
"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/75/af/1ca3bec27e72101585254739189d",
"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/75/af/1ca3bec27e72101585254739189d",
"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/06/ab/699f575fabf3819dcf40b4c3125e",
"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/06/ab/699f575fabf3819dcf40b4c3125e",
"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/b0/55/536be0beef09a49ecca957ca7318",
"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/bd/f3/6f99da945c68fae7f860e69f86c6",
"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/1e/fa/09a99039de3bf810e3ed13959416",
"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/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/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",

View File

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

View File

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

View File

@ -28,11 +28,11 @@
help:
@tools/pcommand makefile_target_list Makefile
# Set env-var BA_ENABLE_IRONY_BUILD_DB=1 to enable creating/updating a cmake
# compile-commands database for use with irony for emacs (and possibly other
# tools).
ifeq ($(BA_ENABLE_IRONY_BUILD_DB),1)
PREREQ_IRONY_BUILD_DB = .cache/irony/compile_commands.json
# Set env-var BA_ENABLE_COMPILE_COMMANDS_DB=1 to enable creating/updating a
# cmake compile-commands database for use with irony for emacs (and possibly
# other tools).
ifeq ($(BA_ENABLE_COMPILE_COMMANDS_DB),1)
PREREQ_COMPILE_COMMANDS_DB = .cache/compile_commands_db/compile_commands.json
endif
# 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
# included in PREREQS_SAFE it would try to build *before* project updates
# 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.
# This installs tool config files, runs environment checks, etc.
@ -1010,20 +1010,20 @@ CMAKE_BUILD_TYPE ?= Debug
# Build and run the 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.
# Sets up the ballistica environment to do things like abort() out to the
# debugger on errors instead of trying to cleanly exit.
cmake-gdb: cmake-build
@cd build/cmake/$(CM_BT_LC)/staged && \
cd build/cmake/$(CM_BT_LC)/staged && \
BA_DEBUGGER_ATTACHED=1 gdb ./ballisticakit
# Build and run the cmake build under the lldb debugger.
# Sets up the ballistica environment to do things like abort() out to the
# debugger on errors instead of trying to cleanly exit.
cmake-lldb: cmake-build
@cd build/cmake/$(CM_BT_LC)/staged && \
cd build/cmake/$(CM_BT_LC)/staged && \
BA_DEBUGGER_ATTACHED=1 lldb ./ballisticakit
# Build but don't run it.
@ -1045,7 +1045,7 @@ cmake-clean:
rm -rf build/cmake/$(CM_BT_LC)
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
@$(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
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
@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)
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
@$(STAGE_BUILD) -cmakemodularserver -$(CM_BT_LC) \
@ -1221,21 +1221,21 @@ ballisticakit-cmake/.clang-format: .clang-format
@mkdir -p ballisticakit-cmake
@cd ballisticakit-cmake && ln -sf ../.clang-format .
# Irony in emacs requires us to use cmake to generate a full
# list of compile commands for all files; lets try to keep it up to date
# Various tools such as Irony for Emacs or clangd make use of a list of
# compile commands for all files; lets try to keep it up to date
# whenever CMakeLists changes.
.cache/irony/compile_commands.json: ballisticakit-cmake/CMakeLists.txt
@tools/pcommand echo BLU Updating Irony build commands db...
@echo Generating Irony compile-commands-list...
@mkdir -p .cache/irony
@cd .cache/irony \
.cache/compile_commands_db/compile_commands.json: \
ballisticakit-cmake/CMakeLists.txt
@tools/pcommand echo BLU Updating compile commands db...
@mkdir -p .cache/compile_commands_db
@cd .cache/compile_commands_db \
&& cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug \
$(shell pwd)/ballisticakit-cmake
@mv .cache/irony/compile_commands.json . \
&& rm -rf .cache/irony \
&& mkdir .cache/irony \
&& mv compile_commands.json .cache/irony
@tools/pcommand echo BLU Created Irony build db at $@
@mv .cache/compile_commands_db/compile_commands.json . \
&& rm -rf .cache/compile_commands_db \
&& mkdir .cache/compile_commands_db \
&& mv compile_commands.json .cache/compile_commands_db
@tools/pcommand echo BLU Created compile commands db at $@
_windows-wsl-build:
@tools/pcommand wsl_build_check_win_drive

View File

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

View File

@ -2,7 +2,8 @@
;;; 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.
((c++-mode (eval . (flycheck-mode)))
(
;; (c++-mode (eval . (flycheck-mode)))
(python-ts-mode (jedi:server-args . ("--sys-path" "__EFRO_PROJECT_ROOT__/tools"
@ -22,8 +23,18 @@
(nil . ((projectile-globally-ignored-directories . ("docs"
"submodules"
"src/external"
"src/assets/ba_data/python-site-packages"
"src/assets/pylib-android"
"src/assets/pylib-apple"
"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,
fatal_error,
get_display_resolution,
get_immediate_return_code,
get_low_level_config_value,
get_max_graphics_quality,
get_replays_dir,
@ -204,6 +205,7 @@ __all__ = [
'fatal_error',
'garbage_collect',
'get_display_resolution',
'get_immediate_return_code',
'get_ip_address_type',
'get_low_level_config_value',
'get_max_graphics_quality',

View File

@ -156,11 +156,11 @@ def on_app_launching() -> None:
assert _babase.in_logic_thread()
# Let the user know if the app Python dir is a custom one.
user_sys_scripts_dir = baenv.get_user_system_scripts_dir()
if user_sys_scripts_dir is not None:
# Let the user know if the app Python dir is a 'user' one.
envconfig = baenv.get_config()
if envconfig.is_user_app_python_dir:
_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),
)

View File

@ -24,20 +24,19 @@ from dataclasses import dataclass
from typing import TYPE_CHECKING
import __main__
from efro.log import setup_logging, LogLevel
if TYPE_CHECKING:
from efro.log import LogHandler
# 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
# the job of this module to set up paths for an engine run, and that may
# involve modifying sys.path in such a way that this module resolves to
# a different path afterwards (for example from
# the job of this module to set up Python paths for an engine run, and
# that may involve modifying sys.path in such a way that this module
# resolves to a different path afterwards (for example from
# /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'
# 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.
#
@ -51,13 +50,13 @@ if TYPE_CHECKING:
# Build number and version of the ballistica binary we expect to be
# using.
TARGET_BALLISTICA_BUILD = 21196
TARGET_BALLISTICA_BUILD = 21199
TARGET_BALLISTICA_VERSION = '1.7.24'
@dataclass
class EnvConfig:
"""Environment put together by the configure call."""
"""Environment values put together by the configure call."""
config_dir: str
data_dir: str
@ -66,49 +65,45 @@ class EnvConfig:
standard_app_python_dir: str
site_python_dir: str | None
log_handler: LogHandler | None
is_user_app_python_dir: bool
@dataclass
class EnvGlobals:
class _EnvGlobals:
"""Our globals we store in the main module."""
config: EnvConfig | None = None
config_called: bool = False
called_configure: bool = False
paths_set_failed: bool = False
user_system_scripts_dir: str | None = None
modular_main_called: bool = False
@classmethod
def get(cls) -> EnvGlobals:
def get(cls) -> _EnvGlobals:
"""Create/return our singleton."""
name = '_baenv_globals'
envglobals: EnvGlobals | None = getattr(__main__, name, None)
envglobals: _EnvGlobals | None = getattr(__main__, name, None)
if envglobals is None:
envglobals = EnvGlobals()
envglobals = _EnvGlobals()
setattr(__main__, name, 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:
"""Has a config been created?"""
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
return _EnvGlobals.get().config is not None
def get_config() -> EnvConfig:
"""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()
config = envglobals.config
@ -128,7 +123,7 @@ def configure(
site_python_dir: str | None = None,
contains_python_dist: bool = False,
) -> 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
creation. This should be called before any other ballistica modules
@ -136,14 +131,14 @@ def configure(
where those modules get loaded from.
"""
envglobals = EnvGlobals.get()
envglobals = _EnvGlobals.get()
if envglobals.config_called:
if envglobals.called_configure:
raise RuntimeError(
'baenv.configure() has already been called;'
' 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
# Python's stdout/stderr into it. Then we can at least debug
@ -167,6 +162,7 @@ def configure(
data_dir,
config_dir,
standard_app_python_dir,
is_user_app_python_dir,
) = _setup_paths(
user_python_dir,
app_python_dir,
@ -190,6 +186,7 @@ def configure(
standard_app_python_dir=standard_app_python_dir,
site_python_dir=site_python_dir,
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:
from efro.log import setup_logging, LogLevel
log_handler = setup_logging(
log_path=None,
level=LogLevel.DEBUG,
@ -249,11 +248,11 @@ def _setup_paths(
site_python_dir: str | None,
data_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
# Python imports:
envglobals = EnvGlobals.get()
envglobals = _EnvGlobals.get()
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 = 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
# at this point aside from complain and inform for next time.
if '_babase' in sys.modules:
@ -299,7 +301,8 @@ def _setup_paths(
# stuff.
check_dir = Path(user_python_dir, 'sys', TARGET_BALLISTICA_VERSION)
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.
@ -342,6 +345,7 @@ def _setup_paths(
data_dir,
config_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:
# Run a default configure BEFORE importing babase.
# (may affect where babase comes from).
configure()
def extract_arg(args: list[str], names: list[str], is_dir: bool) -> str | None:
"""Given a list of args and an arg name, returns a value.
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__':
_main()
_modular_main()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,14 +5,9 @@
from __future__ import annotations
import weakref
from typing import TYPE_CHECKING
import babase
import bascenev1 as bs
if TYPE_CHECKING:
pass
class RespawnIcon:
"""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.
"""
_MASKTEXSTORENAME = babase.storagename('masktex')
_ICONSSTORENAME = babase.storagename('icons')
_MASKTEXSTORENAME = bs.storagename('masktex')
_ICONSSTORENAME = bs.storagename('icons')
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
on_right, offs_extra, respawn_icons = self._get_context(player)
@ -81,13 +76,13 @@ class RespawnIcon:
attrs={
'v_attach': 'top',
'h_attach': 'right' if on_right else 'left',
'text': babase.Lstr(value=player.getname()),
'text': bs.Lstr(value=player.getname()),
'maxwidth': 100,
'h_align': 'center',
'v_align': 'center',
'shadow': 1.0,
'flatness': 1.0,
'color': babase.safecolor(icon['tint_color']),
'color': bs.safecolor(icon['tint_color']),
'scale': 0.5,
'position': npos,
},
@ -109,7 +104,7 @@ class RespawnIcon:
'shadow': 0.5,
'flatness': 0.5,
'v_attach': 'top',
'color': babase.safecolor(icon['tint_color']),
'color': bs.safecolor(icon['tint_color']),
'text': '',
},
)
@ -118,10 +113,10 @@ class RespawnIcon:
assert self._text.node
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._timer: bs.Timer | None = bs.Timer(
1.0, babase.WeakCall(self._update), repeat=True
1.0, bs.WeakCall(self._update), repeat=True
)
@property
@ -159,7 +154,7 @@ class RespawnIcon:
return on_right, offs_extra, icons
def _update(self) -> None:
remaining = int(round(self._respawn_time - babase.apptime()))
remaining = int(round(self._respawn_time - bs.time()))
if remaining > 0:
assert self._text is not None
if self._text.node:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,7 +7,6 @@ from __future__ import annotations
from dataclasses import dataclass
from typing import TYPE_CHECKING, TypeVar, Generic
import babase
import bauiv1 as bui
if TYPE_CHECKING:
@ -35,7 +34,7 @@ class TabRow(Generic[T]):
def __init__(
self,
parent: bui.Widget,
tabdefs: list[tuple[T, babase.Lstr]],
tabdefs: list[tuple[T, bui.Lstr]],
pos: tuple[float, float],
size: tuple[float, float],
on_select_call: Callable[[T], None] | None = None,
@ -58,7 +57,7 @@ class TabRow(Generic[T]):
size=size,
label=tab_label,
enable_sound=False,
on_activate_call=babase.Call(
on_activate_call=bui.Call(
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 (stress_test_stats_file_ == nullptr) {
assert(g_core);
auto user_python_dir = g_core->platform->GetUserPythonDirectory();
auto user_python_dir = g_core->GetUserPythonDirectory();
if (user_python_dir) {
std::string f_name = *user_python_dir + "/stress_test_stats.csv";
stress_test_stats_file_ =

View File

@ -39,7 +39,7 @@ static const bool kShowPruningInfo = false;
#define PENDING_LOAD_PROCESS_TIME 5
Assets::Assets() {
asset_paths_.emplace_back(g_core->platform->GetDataDirectory() + BA_DIRSLASH
asset_paths_.emplace_back(g_core->GetDataDirectory() + BA_DIRSLASH
+ "ba_data");
for (bool& have_pending_load : have_pending_loads_) {
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
// BaseFeatureSet. This locks in baenv customizations.
g_core->python->ApplyBaEnvConfig();
g_core->ApplyBaEnvConfig();
// Create our feature-set's C++ front-end.
assert(g_base == nullptr);

View File

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

View File

@ -711,12 +711,9 @@ static auto PyEnv(PyObject* self) -> PyObject* {
default:
throw Exception();
}
std::optional<std::string> user_py_dir =
g_core->platform->GetUserPythonDirectory();
std::optional<std::string> app_py_dir =
g_core->platform->GetAppPythonDirectory();
std::optional<std::string> site_py_dir =
g_core->platform->GetSitePythonDirectory();
std::optional<std::string> user_py_dir = g_core->GetUserPythonDirectory();
std::optional<std::string> app_py_dir = g_core->GetAppPythonDirectory();
std::optional<std::string> site_py_dir = g_core->GetSitePythonDirectory();
// clang-format off
PyObject* env = Py_BuildValue(
@ -772,7 +769,7 @@ static auto PyEnv(PyObject* self) -> PyObject* {
"device_name",
g_core->platform->GetDeviceName().c_str(),
"data_directory",
g_core->platform->GetDataDirectory().c_str());
g_core->GetDataDirectory().c_str());
// clang-format on
g_base->python->StoreEnv(env);
}
@ -1449,6 +1446,29 @@ static PyMethodDef PyEmptyAppModeHandleIntentExecDef = {
"(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> {
@ -1496,6 +1516,7 @@ auto PythonMethodsApp::GetMethods() -> std::vector<PyMethodDef> {
PyEmptyAppModeDeactivateDef,
PyEmptyAppModeHandleIntentDefaultDef,
PyEmptyAppModeHandleIntentExecDef,
PyGetImmediateReturnCodeDef,
};
}

View File

@ -854,7 +854,7 @@ static PyMethodDef PySetPlatformMiscReadValsDef = {
static auto PyGetLogFilePath(PyObject* self, PyObject* args) -> PyObject* {
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";
return PyUnicode_FromString(logpath.c_str());
BA_PYTHON_CATCH;

View File

@ -15,21 +15,46 @@ CoreFeatureSet* g_core{};
BaseSoftInterface* g_base_soft{};
auto CoreFeatureSet::Import(const CoreConfig* config) -> CoreFeatureSet* {
// Only accept a config in monolithic builds if this is the first import.
if (config != nullptr) {
if (!g_buildconfig.monolithic_build()) {
FatalError("CoreConfig can only be passed in monolithic builds.");
}
if (g_core != nullptr) {
FatalError("CoreConfig can only be passed on the first import call.");
}
if (g_core == nullptr) {
DoImport(*config);
// In monolithic builds we can accept an explicit core-config the first
// time we're imported.
if (g_buildconfig.monolithic_build()) {
if (config != nullptr) {
if (g_core != nullptr) {
FatalError(
"CoreConfig can only be passed on the first CoreFeatureSet::Import "
"call.");
}
if (g_core == nullptr) {
DoImport(*config);
}
} else {
// No config passed; use a default.
if (g_core == nullptr) {
DoImport({});
}
}
} 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) {
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;
@ -90,14 +115,88 @@ void CoreFeatureSet::PostInit() {
// (so that our log handling system is fully bootstrapped), but
// 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.
if (!g_core->core_config().hold_early_logs) {
if (!core_config_.hold_early_logs) {
python->EnablePythonLoggingCalls();
}
}
// FIXME: MOVE THIS TO A RUN_APP_TO_COMPLETION() SORT OF PLACE.
// For now it does the right thing here since all we have is monolithic
// builds but this will need to account for more situations later.
// python->ReleaseMainThreadGIL();
auto CoreFeatureSet::core_config() const -> const CoreConfig& {
// Try to make a bit of noise if we're accessed in modular builds before
// baenv values are set, since in that case we won't yet have our final
// 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 {

View File

@ -3,6 +3,7 @@
#ifndef BALLISTICA_CORE_CORE_H_
#define BALLISTICA_CORE_CORE_H_
#include <list>
#include <mutex>
#include <string>
#include <thread>
@ -54,13 +55,15 @@ class CoreFeatureSet {
auto SoftImportBase() -> BaseSoftInterface*;
/// 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.
/// Can be used during shutdown or when trying to send a crash-report to
/// ensure we don't hang indefinitely.
void StartSuicideTimer(const std::string& action, millisecs_t delay);
void ApplyBaEnvConfig();
// Call this if the main thread changes.
// Fixme: Should come up with something less hacky feeling.
void UpdateMainThreadID();
@ -108,6 +111,38 @@ class CoreFeatureSet {
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.
CorePython* const python;
CorePlatform* const platform;
@ -159,6 +194,13 @@ class CoreFeatureSet {
std::mutex app_time_mutex_;
std::string legacy_user_agent_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

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
// the user can tamper with it.
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")) {
// There's an existing one; read it.
@ -201,13 +201,14 @@ auto CorePlatform::DoGetConfigDirectoryMonolithicDefault()
}
auto CorePlatform::GetConfigFilePath() -> std::string {
return GetConfigDirectory() + BA_DIRSLASH + "config.json";
return g_core->GetConfigDirectory() + BA_DIRSLASH + "config.json";
}
// FIXME: should make this unnecessary.
auto CorePlatform::GetLowLevelConfigValue(const char* key, int default_value)
-> int {
std::string path = GetConfigDirectory() + BA_DIRSLASH + ".cvar_" + key;
std::string path =
g_core->GetConfigDirectory() + BA_DIRSLASH + ".cvar_" + key;
int val = default_value;
FILE* f = FOpen(path.c_str(), "r");
if (f) {
@ -225,7 +226,8 @@ auto CorePlatform::GetLowLevelConfigValue(const char* key, int default_value)
// FIXME: should make this unnecessary.
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);
FILE* f = FOpen(path.c_str(), "w");
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 {
if (!made_volatile_data_dir_) {
volatile_data_dir_ = GetDefaultVolatileDataDirectory();
@ -254,23 +251,13 @@ auto CorePlatform::GetVolatileDataDirectory() -> std::string {
auto CorePlatform::GetDefaultVolatileDataDirectory() -> std::string {
// By default, stuff this in a subdir under our config dir.
return 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_;
return g_core->GetConfigDirectory() + BA_DIRSLASH + "vdata";
}
auto CorePlatform::GetReplaysDir() -> std::string {
static bool made_dir = false;
if (!made_dir) {
replays_dir_ = GetConfigDirectory() + BA_DIRSLASH + "replays";
replays_dir_ = g_core->GetConfigDirectory() + BA_DIRSLASH + "replays";
MakeDir(replays_dir_);
made_dir = true;
}
@ -355,13 +342,6 @@ auto CorePlatform::GetErrnoString() -> std::string {
#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()
-> std::optional<std::string> {
// CoreConfig value trumps all. Otherwise go with platform-specific default.
@ -371,11 +351,6 @@ auto CorePlatform::GetConfigDirectoryMonolithicDefault()
return DoGetConfigDirectoryMonolithicDefault();
}
auto CorePlatform::GetDataDirectory() -> std::string {
BA_PRECONDITION(have_ba_env_vals_);
return ba_env_data_dir_;
}
auto CorePlatform::GetDataDirectoryMonolithicDefault() -> std::string {
// CoreConfig arg trumps all. Otherwise ask for platform-specific value.
if (g_core->core_config().data_dir.has_value()) {
@ -1216,32 +1191,4 @@ auto CorePlatform::System(const char* cmd) -> int {
#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

View File

@ -141,35 +141,16 @@ class CorePlatform {
/// etc).
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.
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>;
/// Get the path of the app config file.
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>;
/// 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
/// directory should not be included in backups and the app should remain
/// 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;
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?).
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:
/// Are we being run from a terminal? (should we show prompts, etc?).
virtual auto GetIsStdinATerminal() -> bool;
@ -544,7 +514,6 @@ class CorePlatform {
private:
bool is_stdin_a_terminal_{};
bool using_custom_app_python_dir_{};
bool have_has_touchscreen_value_{};
bool have_touchscreen_{};
bool is_tegra_k1_{};
@ -553,17 +522,11 @@ class CorePlatform {
bool made_volatile_data_dir_{};
bool have_device_uuid_{};
bool ran_base_post_init_{};
bool have_ba_env_vals_{};
millisecs_t start_time_millisecs_{};
std::string device_name_;
std::string legacy_device_uuid_;
std::string volatile_data_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

View File

@ -15,13 +15,6 @@ void LowLevelPythonDebugLog(const char* 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) {
if (PyStatus_Exception(status)) {
FatalError(std::string("Error in ") + where + ": "
@ -337,4 +330,76 @@ void CorePython::LoggingCall(LogLevel loglevel, const std::string& msg) {
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

View File

@ -43,10 +43,6 @@ class CorePython {
/// pent up ones) to Python.
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.)
/// Can be called from any thread at any time. If called before Python
/// logging is available, logs locally using Logging::DisplayLog()
@ -56,6 +52,13 @@ class CorePython {
void VerifyPythonEnvironment();
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_; }
private:

View File

@ -75,45 +75,36 @@ static auto ParseArgValue(int argc, char** argv, int* i, const char* arg_long,
return {};
}
auto CoreConfig::FromCommandLineAndEnv(int argc, char** argv) -> CoreConfig {
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.
void CoreConfig::ApplyEnvVars() {
if (auto* envval = getenv("BA_LIFECYCLE_LOG")) {
if (!strcmp(envval, "1")) {
cfg.lifecycle_log = true;
lifecycle_log = true;
}
}
if (auto* envval = getenv("BA_DEBUGGER_ATTACHED")) {
if (!strcmp(envval, "1")) {
cfg.debugger_attached = true;
debugger_attached = true;
}
}
if (auto* envval = getenv("BA_DEBUG_TIMING")) {
if (!strcmp(envval, "1")) {
cfg.debug_timing = true;
debug_timing = true;
}
}
}
// REMOVE ME FOR 1.7.20 FINAL.
if (explicit_bool(false)) {
printf("TEMP: forcing BA_LIFECYCLE_LOG=1 during 1.7.20 development.\n");
cfg.lifecycle_log = true;
}
void CoreConfig::ApplyArgs(int argc, char** argv) {
try {
// First handle single-arg special cases like --help or --version.
if (IsSingleArgSpecialCase(argc, argv, "--help", "-h")) {
PrintHelp();
cfg.immediate_return_code = 0;
return cfg;
immediate_return_code = 0;
return;
}
if (IsSingleArgSpecialCase(argc, argv, "--version", "-v")) {
printf("BallisticaKit %s build %d\n", kEngineVersion, kEngineBuildNumber);
cfg.immediate_return_code = 0;
return cfg;
immediate_return_code = 0;
return;
}
if (IsSingleArgSpecialCase(argc, argv, "--crash")) {
int dummyval{};
@ -126,7 +117,7 @@ auto CoreConfig::FromCommandLineAndEnv(int argc, char** argv) -> CoreConfig {
if (explicit_bool(true)) {
*invalid_ptr = 1;
}
return cfg;
return;
}
// 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;
while (i < argc) {
if ((value = ParseArgValue(argc, argv, &i, "--command", "-c"))) {
cfg.call_command = *value;
call_command = *value;
} else if ((value = ParseArgValue(argc, argv, &i, "--exec", "-e"))) {
cfg.exec_command = *value;
exec_command = *value;
} else if ((value =
ParseArgValue(argc, argv, &i, "--config-dir", "-C"))) {
cfg.config_dir = *value;
config_dir = *value;
// Make sure what they passed exists.
// 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
// being more strict is ok when accepting user input.
if (!std::filesystem::exists(*cfg.config_dir)) {
printf("Error: Provided config dir does not exist: '%s'.",
cfg.config_dir->c_str());
if (!std::filesystem::is_directory(*config_dir)) {
printf("Error: Provided config-dir path '%s' is not a directory.",
config_dir->c_str());
throw BadArgsException();
}
} else if ((value = ParseArgValue(argc, argv, &i, "--data-dir", "-d"))) {
cfg.data_dir = *value;
data_dir = *value;
// Make sure what they passed exists.
if (!std::filesystem::exists(*cfg.data_dir)) {
printf("Error: Provided data dir does not exist: '%s'.",
cfg.data_dir->c_str());
if (!std::filesystem::is_directory(*data_dir)) {
printf("Error: Provided data-dir path '%s' is not a directory.",
data_dir->c_str());
throw BadArgsException();
}
} 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.
if (!std::filesystem::exists(*cfg.user_python_dir)) {
printf("Error: Provided mods dir does not exist: '%s'.",
cfg.user_python_dir->c_str());
if (!std::filesystem::is_directory(*user_python_dir)) {
printf("Error: Provided mods-dir path '%s' is not a directory.",
user_python_dir->c_str());
throw BadArgsException();
}
} else {
@ -175,8 +166,25 @@ auto CoreConfig::FromCommandLineAndEnv(int argc, char** argv) -> CoreConfig {
}
}
} 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;
}

View File

@ -14,7 +14,17 @@ namespace ballistica::core {
/// when initing the core feature-set.
class CoreConfig {
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.
bool vr_mode{};
@ -26,19 +36,19 @@ class CoreConfig {
std::optional<int> immediate_return_code{};
/// 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{};
/// Python command to be run within the normal app loop.
std::optional<std::string> exec_command{};
/// Explicitly set config dir.
/// Explicitly passed config dir.
std::optional<std::string> config_dir{};
/// Explicitly set data dir.
/// Explicitly passed 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{};
/// Log various stages/times in the bootstrapping process.

View File

@ -25,7 +25,7 @@
#if BA_MONOLITHIC_BUILD && BA_DEFINE_MAIN
auto main(int argc, char** argv) -> int {
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
// things such as '--help', in which case we're done.
@ -39,7 +39,7 @@ auto main(int argc, char** argv) -> int {
namespace ballistica {
// These are set automatically via script; don't modify them here.
const int kEngineBuildNumber = 21196;
const int kEngineBuildNumber = 21199;
const char* kEngineVersion = "1.7.24";
#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.
l_core = core::CoreFeatureSet::Import(&core_config);
// TEMP - bug hunting.
l_core->platform->DebugLog("mm1");
// If a command was passed, simply run it and exit. We want to act
// simply as a Python interpreter in that case; we don't do any
// environment setup (aside from the bits core does automatically such
@ -80,9 +77,6 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int {
exit(success ? 0 : 1);
}
// TEMP - bug hunting.
l_core->platform->DebugLog("mm2");
// Ok, looks like we're doing a standard monolithic-mode app run.
// -------------------------------------------------------------------------
@ -95,9 +89,6 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int {
// those modules get loaded from in the first place.
l_core->python->MonolithicModeBaEnvConfigure();
// TEMP - bug hunting.
l_core->platform->DebugLog("mm3");
// We need the base feature-set to run a full app but we don't have a hard
// dependency to it. Let's see if it's available.
l_base = l_core->SoftImportBase();
@ -109,9 +100,6 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int {
// Phase 2: "The pieces are moving."
// -------------------------------------------------------------------------
// TEMP - bug hunting.
l_core->platform->DebugLog("mm4");
// Spin up all app machinery such as threads and subsystems. This gets
// things ready to rock, but there's no actual rocking quite yet.
l_base->StartApp();
@ -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."
// -------------------------------------------------------------------------
// TEMP - bug hunting.
l_core->platform->DebugLog("mm5");
// At this point we unleash the beast and then simply process events
// until the app exits (or we return from this function and let the
// environment do that part).
@ -165,7 +150,6 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int {
}
}
}
if (l_core) {
l_core->platform->WillExitMain(false);
return l_core->return_value;

View File

@ -105,7 +105,8 @@ auto Python::GetPyString(PyObject* o) -> std::string {
return PyUnicode_AsUTF8(o);
}
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>
@ -220,6 +221,24 @@ auto Python::GetPyFloats(PyObject* o) -> std::vector<float> {
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>
auto GetPyIntsT(PyObject* o) -> std::vector<T> {
assert(Python::HaveGIL());

View File

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

View File

@ -12,14 +12,14 @@ namespace ballistica {
// String based Python commands.
// 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
// output instead of printing it, etc. Now, however, its usage is pretty
// much limited to a few places such as handling stdin and the in-game console.
// (Most places it is much cleaner to work with proper python modules and just
// interact with PyObject* refs to them)
// I should look and see if python's default high level calls would suffice
// for these purposes and potentially kill this off.
// 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 output instead of printing it, etc. Now, however, its usage is
// pretty much limited to a few places such as handling stdin and the
// in-app console. (Most places it is much cleaner to work with proper
// python modules and just interact with PyObject* refs to them) I should
// look and see if python's default high level calls would suffice for these
// purposes and potentially kill this off.
class PythonCommand {
public:
PythonCommand();

View File

@ -4,6 +4,7 @@
#include "ballistica/core/python/core_python.h"
#include "ballistica/core/support/base_soft.h"
#include "ballistica/shared/foundation/types.h"
#include "ballistica/shared/math/vector2f.h"
#include "ballistica/shared/python/python.h"
#include "ballistica/shared/python/python_sys.h"
@ -13,7 +14,6 @@ namespace ballistica {
// Note: implicitly using core globals here; our behavior is undefined
// if core has not been imported by anyone yet.
using core::g_base_soft;
using core::g_core;
// Ignore a few things that python macros do.
#pragma clang diagnostic push
@ -179,6 +179,16 @@ auto PythonRef::ValueAsOptionalString() const -> std::optional<std::string> {
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 {
assert(Python::HaveGIL());
ThrowIfUnset();
@ -238,15 +248,8 @@ auto PythonRef::UnicodeCheck() const -> bool {
return static_cast<bool>(PyUnicode_Check(obj_));
}
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);
static inline auto _HandleCallResults(PyObject* out, bool print_errors)
-> PyObject* {
if (!out) {
if (print_errors) {
// 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();
}
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();
}
auto PythonRef::Call() const -> PythonRef {
// NOTE: Using core globals directly here; normally don't do this.
assert(g_core);
return Call(
g_core->python->objs().Get(core::CorePython::ObjID::kEmptyTuple).Get());
auto PythonRef::Call(bool print_errors) const -> PythonRef {
assert(obj_);
assert(Python::HaveGIL());
assert(CallableCheck());
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());
PythonRef args(Py_BuildValue("((ff))", val.x, val.y), PythonRef::kSteal);
return Call(args);
return Call(args.Get(), nullptr, print_errors);
}
PythonRef::~PythonRef() { Release(); }

View File

@ -3,6 +3,7 @@
#ifndef BALLISTICA_SHARED_PYTHON_PYTHON_REF_H_
#define BALLISTICA_SHARED_PYTHON_PYTHON_REF_H_
#include <list>
#include <optional>
#include <string>
@ -166,6 +167,8 @@ class PythonRef {
auto ValueAsString() const -> std::string;
auto ValueAsOptionalString() const -> std::optional<std::string>;
auto ValueAsOptionalStringSequence() const
-> std::optional<std::list<std::string>>;
auto ValueAsInt() const -> int64_t;
@ -185,10 +188,10 @@ class PythonRef {
bool print_errors = true) const -> PythonRef {
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.
auto Call(const Vector2f& val) const -> PythonRef;
auto Call(const Vector2f& val, bool print_errors = true) const -> PythonRef;
private:
void ThrowIfUnset() const;

View File

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

View File

@ -105,7 +105,7 @@ class Pruner:
self.paths = [os.path.abspath(p) for p in self.paths]
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):
raise CleanError(
f'Compile-commands not found at "{cmdspath}".'

View File

@ -786,34 +786,16 @@ class SpinoffContext:
if 'base' in self._src_omit_feature_sets:
text = replace_exact(
text,
(
'def _main() -> None:\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'
),
(
'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'
),
' import babase\n',
' # (Hack; spinoff disabled babase).\n'
' if TYPE_CHECKING:\n'
' from typing import Any\n'
'\n'
' # import babase\n'
'\n'
' babase: Any = None\n'
' if bool(True):\n'
" raise CleanError('babase not present')\n",
label=src_path,
)
@ -1587,7 +1569,7 @@ class SpinoffContext:
os.chmod(dst_path_full, mode)
else:
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

View File

@ -526,7 +526,7 @@ class AssetStager:
'# Basically this will do:\n'
'# import baenv; baenv.configure();'
' 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)

View File

@ -25,16 +25,18 @@ class CleanError(Exception):
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.
If the error has an empty message, prints nothing (not even a newline).
"""
from efro.terminal import Clr
if prefix:
prefix = f'{prefix}: '
errstr = str(self)
if errstr:
print(f'{Clr.SRED}{errstr}{Clr.RST}', flush=flush)
print(f'{Clr.SRED}{prefix}{errstr}{Clr.RST}', flush=flush)
class CommunicationError(Exception):