work on app-modes and app-intents

This commit is contained in:
Eric 2023-05-26 18:42:17 -07:00
parent 4e21714961
commit 28da551aa2
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
40 changed files with 607 additions and 111 deletions

View File

@ -4072,26 +4072,26 @@
"build/assets/workspace/ninjafightplug.py": "https://files.ballistica.net/cache/ba1/c5/09/4f10b8a21ba87aa5509cff7a164b", "build/assets/workspace/ninjafightplug.py": "https://files.ballistica.net/cache/ba1/c5/09/4f10b8a21ba87aa5509cff7a164b",
"build/assets/workspace/onslaughtplug.py": "https://files.ballistica.net/cache/ba1/ff/0a/a354984f9c074dab0676ac7e4877", "build/assets/workspace/onslaughtplug.py": "https://files.ballistica.net/cache/ba1/ff/0a/a354984f9c074dab0676ac7e4877",
"build/assets/workspace/runaroundplug.py": "https://files.ballistica.net/cache/ba1/2a/1c/9ee5db6d1bceca7fa6638fb8abde", "build/assets/workspace/runaroundplug.py": "https://files.ballistica.net/cache/ba1/2a/1c/9ee5db6d1bceca7fa6638fb8abde",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/7b/fc/40202144638c394e88af0424ac38", "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/08/e3/a0b9223475c7706681fad7968ba7",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/bd/fb/9a2813b5a23b727b1b246d5b1a31", "build/prefab/full/linux_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/6d/ef/b6d1a6b9754d3036f71ad3fa77e2",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/6f/99/9e5c25105d2bc8fd1db0402cdb0f", "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/2b/b1/6f30bc9be42939f9399d4a3f2b3a",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/8b/4e/f6496d2ccd7a869e4e9d2eb57ee4", "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/0e/ef/1ce03a67174a21bb153ea7e8e27b",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/f6/ec/dab1171b1c99587a80e959dddaa5", "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/d0/26/46d5ff486b95aedaa3caf3c299c9",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/2f/23/0d5dd4dd3dd190a3f81eca6d11f0", "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/10/ea/b9aa9951192cbc45cccb4c3c2289",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/f3/ec/b238f5118def4ecac3ba4363f389", "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/e0/a0/32c52b4e83da434e22544bdda893",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/b2/d2/307b223ce8182e3c14a982dad7d4", "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/0b/09/2986300ce8c70c4e301da76a0533",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/08/67/672c0b7edb83bd06f3f08d25c1d1", "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/b1/79/ba61f157a98f4ceab818d99edab9",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/5e/34/7189db32e52d599755bf59587671", "build/prefab/full/mac_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/09/78/0a5975081c617f7a52c6ddd3d7c0",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/ce/44/dec692802d8023cd48f5f29fa40d", "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/36/91/561ed25a53dd05a321418ef95527",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/8d/52/084e0817f587f035017bc2595a8d", "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/8d/27/ee747a3e85a28e124c21ac2b380d",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/46/33/1975fe658fc9ffc96b95c0071ecd", "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/a7/b8/ad830a02ee58f2e87466f011fb27",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/66/ea/4e901a9ad2ad965bdc773ee3b127", "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/ca/71/6fd4afebc57e78d5986a2f657a3f",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/2f/54/565f3150df759bffea9dcad21685", "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/56/16/efa353be8adca6c233db12102463",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/52/a5/594f6db4b4f0d8d9937bb67d80f6", "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/85/98/0aa74afa4bfa7f12b4bb54fe76a3",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/0f/86/5228cc1d4a8d2317585028e115f3", "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/e6/bb/308f33d3c4b549b8b701a6e94db1",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/73/b0/83918095162a68c12bffdb7a4d97", "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/68/c5/ad76162d865a7a14f2f2677300d1",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/12/55/5d5b5a467a1c2ec10c2551465682", "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/c6/39/21bdc2e4e59855558bbc7d606f28",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/48/95/a35e7407f5f813cea477dd75130f", "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/f9/eb/e2be79806312a8615a1e6da93078",
"build/prefab/lib/linux_arm64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/f4/e7/32cfbd534ac61e626f6c6fbe2b01", "build/prefab/lib/linux_arm64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/f4/e7/32cfbd534ac61e626f6c6fbe2b01",
"build/prefab/lib/linux_arm64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/f3/f8/52577356f2ff5229ed4e5a6764cf", "build/prefab/lib/linux_arm64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/f3/f8/52577356f2ff5229ed4e5a6764cf",
"build/prefab/lib/linux_arm64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/60/2b/35acb337afc2d997ffc94d2da757", "build/prefab/lib/linux_arm64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/60/2b/35acb337afc2d997ffc94d2da757",
@ -4108,14 +4108,14 @@
"build/prefab/lib/mac_x86_64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/30/4a/aa281e0eb46722098ec29d7da4f8", "build/prefab/lib/mac_x86_64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/30/4a/aa281e0eb46722098ec29d7da4f8",
"build/prefab/lib/mac_x86_64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/0b/e4/a9d278c1bc9a5d731f865ac91a0b", "build/prefab/lib/mac_x86_64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/0b/e4/a9d278c1bc9a5d731f865ac91a0b",
"build/prefab/lib/mac_x86_64_server/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/92/f7/8898478ab4ef0a342c727dd64195", "build/prefab/lib/mac_x86_64_server/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/92/f7/8898478ab4ef0a342c727dd64195",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/df/9f/960a5370e02cae685ae3452066f9", "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/14/3f/8bffa39ee2e862bfd61886f8dd20",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/7d/f7/ba5e3f57b5c6fe96542f61ae21f1", "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/de/69/ff2d73feab522f0be693900c534b",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/34/b3/83428e86252522a10d0c097fc3eb", "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/2a/ac/8e4a2b322079d57a65cf5ec686eb",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/1d/31/12a6e62fc12477b4ed1eb3735b08", "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/9b/d8/f5dbc2b76e5d6992e6e0f957699d",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/65/ca/8bd967cebd1836e03117c2b10f5d", "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/68/f9/0e55e33d9782d596d65770e943d8",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/8c/41/4689d7ca391e92fed73b81e5064c", "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/b4/49/32ebdaa15a795c84e90c3d16fdee",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/7b/a9/f53d5f009e6cc89a265c076bbb51", "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/e9/3c/b86de4797400df2a7bbba4e957f7",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/d1/ed/acae7564f9d534d7f78a3a9373ee", "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/93/88/176702f3743aa143dcd2ee651c4a",
"src/assets/ba_data/python/babase/_mgen/__init__.py": "https://files.ballistica.net/cache/ba1/52/c6/c11130af7b10d6c0321add5518fa", "src/assets/ba_data/python/babase/_mgen/__init__.py": "https://files.ballistica.net/cache/ba1/52/c6/c11130af7b10d6c0321add5518fa",
"src/assets/ba_data/python/babase/_mgen/enums.py": "https://files.ballistica.net/cache/ba1/38/c3/1dedd5e74f2508efc5974c8815a1", "src/assets/ba_data/python/babase/_mgen/enums.py": "https://files.ballistica.net/cache/ba1/38/c3/1dedd5e74f2508efc5974c8815a1",
"src/ballistica/base/mgen/pyembed/binding_base.inc": "https://files.ballistica.net/cache/ba1/b4/3d/e352190a0e5673d101c0f3ee3ad2", "src/ballistica/base/mgen/pyembed/binding_base.inc": "https://files.ballistica.net/cache/ba1/b4/3d/e352190a0e5673d101c0f3ee3ad2",

View File

@ -1,4 +1,4 @@
### 1.7.20 (build 21022, api 8, 2023-05-24) ### 1.7.20 (build 21023, api 8, 2023-05-26)
- This seems like a good time for a `refactoring` release in anticipation of - This seems like a good time for a `refactoring` release in anticipation of
changes coming in 1.8. Basically this means that a lot of things will be changes coming in 1.8. Basically this means that a lot of things will be

View File

@ -408,6 +408,7 @@ add_executable(ballisticakit
${BA_SRC_ROOT}/ballistica/base/python/support/python_context_call.h ${BA_SRC_ROOT}/ballistica/base/python/support/python_context_call.h
${BA_SRC_ROOT}/ballistica/base/python/support/python_context_call_runnable.h ${BA_SRC_ROOT}/ballistica/base/python/support/python_context_call_runnable.h
${BA_SRC_ROOT}/ballistica/base/support/app_timer.h ${BA_SRC_ROOT}/ballistica/base/support/app_timer.h
${BA_SRC_ROOT}/ballistica/base/support/classic_soft.h
${BA_SRC_ROOT}/ballistica/base/support/context.cc ${BA_SRC_ROOT}/ballistica/base/support/context.cc
${BA_SRC_ROOT}/ballistica/base/support/context.h ${BA_SRC_ROOT}/ballistica/base/support/context.h
${BA_SRC_ROOT}/ballistica/base/support/huffman.cc ${BA_SRC_ROOT}/ballistica/base/support/huffman.cc

View File

@ -399,6 +399,7 @@
<ClInclude Include="..\..\src\ballistica\base\python\support\python_context_call.h" /> <ClInclude Include="..\..\src\ballistica\base\python\support\python_context_call.h" />
<ClInclude Include="..\..\src\ballistica\base\python\support\python_context_call_runnable.h" /> <ClInclude Include="..\..\src\ballistica\base\python\support\python_context_call_runnable.h" />
<ClInclude Include="..\..\src\ballistica\base\support\app_timer.h" /> <ClInclude Include="..\..\src\ballistica\base\support\app_timer.h" />
<ClInclude Include="..\..\src\ballistica\base\support\classic_soft.h" />
<ClCompile Include="..\..\src\ballistica\base\support\context.cc" /> <ClCompile Include="..\..\src\ballistica\base\support\context.cc" />
<ClInclude Include="..\..\src\ballistica\base\support\context.h" /> <ClInclude Include="..\..\src\ballistica\base\support\context.h" />
<ClCompile Include="..\..\src\ballistica\base\support\huffman.cc" /> <ClCompile Include="..\..\src\ballistica\base\support\huffman.cc" />

View File

@ -631,6 +631,9 @@
<ClInclude Include="..\..\src\ballistica\base\support\app_timer.h"> <ClInclude Include="..\..\src\ballistica\base\support\app_timer.h">
<Filter>ballistica\base\support</Filter> <Filter>ballistica\base\support</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ballistica\base\support\classic_soft.h">
<Filter>ballistica\base\support</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\support\context.cc"> <ClCompile Include="..\..\src\ballistica\base\support\context.cc">
<Filter>ballistica\base\support</Filter> <Filter>ballistica\base\support</Filter>
</ClCompile> </ClCompile>

View File

@ -394,6 +394,7 @@
<ClInclude Include="..\..\src\ballistica\base\python\support\python_context_call.h" /> <ClInclude Include="..\..\src\ballistica\base\python\support\python_context_call.h" />
<ClInclude Include="..\..\src\ballistica\base\python\support\python_context_call_runnable.h" /> <ClInclude Include="..\..\src\ballistica\base\python\support\python_context_call_runnable.h" />
<ClInclude Include="..\..\src\ballistica\base\support\app_timer.h" /> <ClInclude Include="..\..\src\ballistica\base\support\app_timer.h" />
<ClInclude Include="..\..\src\ballistica\base\support\classic_soft.h" />
<ClCompile Include="..\..\src\ballistica\base\support\context.cc" /> <ClCompile Include="..\..\src\ballistica\base\support\context.cc" />
<ClInclude Include="..\..\src\ballistica\base\support\context.h" /> <ClInclude Include="..\..\src\ballistica\base\support\context.h" />
<ClCompile Include="..\..\src\ballistica\base\support\huffman.cc" /> <ClCompile Include="..\..\src\ballistica\base\support\huffman.cc" />

View File

@ -631,6 +631,9 @@
<ClInclude Include="..\..\src\ballistica\base\support\app_timer.h"> <ClInclude Include="..\..\src\ballistica\base\support\app_timer.h">
<Filter>ballistica\base\support</Filter> <Filter>ballistica\base\support</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ballistica\base\support\classic_soft.h">
<Filter>ballistica\base\support</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\support\context.cc"> <ClCompile Include="..\..\src\ballistica\base\support\context.cc">
<Filter>ballistica\base\support</Filter> <Filter>ballistica\base\support</Filter>
</ClCompile> </ClCompile>

View File

@ -6,6 +6,9 @@
"ba_data/python/babase/__pycache__/_app.cpython-311.opt-1.pyc", "ba_data/python/babase/__pycache__/_app.cpython-311.opt-1.pyc",
"ba_data/python/babase/__pycache__/_appcomponent.cpython-311.opt-1.pyc", "ba_data/python/babase/__pycache__/_appcomponent.cpython-311.opt-1.pyc",
"ba_data/python/babase/__pycache__/_appconfig.cpython-311.opt-1.pyc", "ba_data/python/babase/__pycache__/_appconfig.cpython-311.opt-1.pyc",
"ba_data/python/babase/__pycache__/_appintent.cpython-311.opt-1.pyc",
"ba_data/python/babase/__pycache__/_appmode.cpython-311.opt-1.pyc",
"ba_data/python/babase/__pycache__/_appmodeselector.cpython-311.opt-1.pyc",
"ba_data/python/babase/__pycache__/_apputils.cpython-311.opt-1.pyc", "ba_data/python/babase/__pycache__/_apputils.cpython-311.opt-1.pyc",
"ba_data/python/babase/__pycache__/_assetmanager.cpython-311.opt-1.pyc", "ba_data/python/babase/__pycache__/_assetmanager.cpython-311.opt-1.pyc",
"ba_data/python/babase/__pycache__/_asyncio.cpython-311.opt-1.pyc", "ba_data/python/babase/__pycache__/_asyncio.cpython-311.opt-1.pyc",
@ -27,6 +30,9 @@
"ba_data/python/babase/_app.py", "ba_data/python/babase/_app.py",
"ba_data/python/babase/_appcomponent.py", "ba_data/python/babase/_appcomponent.py",
"ba_data/python/babase/_appconfig.py", "ba_data/python/babase/_appconfig.py",
"ba_data/python/babase/_appintent.py",
"ba_data/python/babase/_appmode.py",
"ba_data/python/babase/_appmodeselector.py",
"ba_data/python/babase/_apputils.py", "ba_data/python/babase/_apputils.py",
"ba_data/python/babase/_assetmanager.py", "ba_data/python/babase/_assetmanager.py",
"ba_data/python/babase/_asyncio.py", "ba_data/python/babase/_asyncio.py",
@ -122,6 +128,7 @@
"ba_data/python/bascenev1/__pycache__/_activity.cpython-311.opt-1.pyc", "ba_data/python/bascenev1/__pycache__/_activity.cpython-311.opt-1.pyc",
"ba_data/python/bascenev1/__pycache__/_activitytypes.cpython-311.opt-1.pyc", "ba_data/python/bascenev1/__pycache__/_activitytypes.cpython-311.opt-1.pyc",
"ba_data/python/bascenev1/__pycache__/_actor.cpython-311.opt-1.pyc", "ba_data/python/bascenev1/__pycache__/_actor.cpython-311.opt-1.pyc",
"ba_data/python/bascenev1/__pycache__/_appmode.cpython-311.opt-1.pyc",
"ba_data/python/bascenev1/__pycache__/_collision.cpython-311.opt-1.pyc", "ba_data/python/bascenev1/__pycache__/_collision.cpython-311.opt-1.pyc",
"ba_data/python/bascenev1/__pycache__/_coopgame.cpython-311.opt-1.pyc", "ba_data/python/bascenev1/__pycache__/_coopgame.cpython-311.opt-1.pyc",
"ba_data/python/bascenev1/__pycache__/_coopsession.cpython-311.opt-1.pyc", "ba_data/python/bascenev1/__pycache__/_coopsession.cpython-311.opt-1.pyc",
@ -152,6 +159,7 @@
"ba_data/python/bascenev1/_activity.py", "ba_data/python/bascenev1/_activity.py",
"ba_data/python/bascenev1/_activitytypes.py", "ba_data/python/bascenev1/_activitytypes.py",
"ba_data/python/bascenev1/_actor.py", "ba_data/python/bascenev1/_actor.py",
"ba_data/python/bascenev1/_appmode.py",
"ba_data/python/bascenev1/_collision.py", "ba_data/python/bascenev1/_collision.py",
"ba_data/python/bascenev1/_coopgame.py", "ba_data/python/bascenev1/_coopgame.py",
"ba_data/python/bascenev1/_coopsession.py", "ba_data/python/bascenev1/_coopsession.py",

View File

@ -139,6 +139,9 @@ SCRIPT_TARGETS_PY_PUBLIC = \
$(BUILD_DIR)/ba_data/python/babase/_app.py \ $(BUILD_DIR)/ba_data/python/babase/_app.py \
$(BUILD_DIR)/ba_data/python/babase/_appcomponent.py \ $(BUILD_DIR)/ba_data/python/babase/_appcomponent.py \
$(BUILD_DIR)/ba_data/python/babase/_appconfig.py \ $(BUILD_DIR)/ba_data/python/babase/_appconfig.py \
$(BUILD_DIR)/ba_data/python/babase/_appintent.py \
$(BUILD_DIR)/ba_data/python/babase/_appmode.py \
$(BUILD_DIR)/ba_data/python/babase/_appmodeselector.py \
$(BUILD_DIR)/ba_data/python/babase/_apputils.py \ $(BUILD_DIR)/ba_data/python/babase/_apputils.py \
$(BUILD_DIR)/ba_data/python/babase/_assetmanager.py \ $(BUILD_DIR)/ba_data/python/babase/_assetmanager.py \
$(BUILD_DIR)/ba_data/python/babase/_asyncio.py \ $(BUILD_DIR)/ba_data/python/babase/_asyncio.py \
@ -188,6 +191,7 @@ SCRIPT_TARGETS_PY_PUBLIC = \
$(BUILD_DIR)/ba_data/python/bascenev1/_activity.py \ $(BUILD_DIR)/ba_data/python/bascenev1/_activity.py \
$(BUILD_DIR)/ba_data/python/bascenev1/_activitytypes.py \ $(BUILD_DIR)/ba_data/python/bascenev1/_activitytypes.py \
$(BUILD_DIR)/ba_data/python/bascenev1/_actor.py \ $(BUILD_DIR)/ba_data/python/bascenev1/_actor.py \
$(BUILD_DIR)/ba_data/python/bascenev1/_appmode.py \
$(BUILD_DIR)/ba_data/python/bascenev1/_collision.py \ $(BUILD_DIR)/ba_data/python/bascenev1/_collision.py \
$(BUILD_DIR)/ba_data/python/bascenev1/_coopgame.py \ $(BUILD_DIR)/ba_data/python/bascenev1/_coopgame.py \
$(BUILD_DIR)/ba_data/python/bascenev1/_coopsession.py \ $(BUILD_DIR)/ba_data/python/bascenev1/_coopsession.py \
@ -405,6 +409,9 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_app.cpython-311.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/babase/__pycache__/_app.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_appcomponent.cpython-311.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/babase/__pycache__/_appcomponent.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_appconfig.cpython-311.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/babase/__pycache__/_appconfig.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_appintent.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_appmode.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_appmodeselector.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_apputils.cpython-311.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/babase/__pycache__/_apputils.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_assetmanager.cpython-311.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/babase/__pycache__/_assetmanager.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_asyncio.cpython-311.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/babase/__pycache__/_asyncio.cpython-311.opt-1.pyc \
@ -454,6 +461,7 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
$(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_activity.cpython-311.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_activity.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_activitytypes.cpython-311.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_activitytypes.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_actor.cpython-311.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_actor.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_appmode.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_collision.cpython-311.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_collision.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_coopgame.cpython-311.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_coopgame.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_coopsession.cpython-311.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_coopsession.cpython-311.opt-1.pyc \

View File

@ -33,6 +33,8 @@ from _babase import (
in_logic_thread, in_logic_thread,
) )
from babase._appintent import AppIntent, AppIntentDefault, AppIntentExec
from babase._appmode import AppMode
from babase._accountv2 import AccountV2Handle from babase._accountv2 import AccountV2Handle
from babase._plugin import PotentialPlugin, Plugin, PluginSubsystem from babase._plugin import PotentialPlugin, Plugin, PluginSubsystem
from babase._app import App from babase._app import App
@ -155,6 +157,10 @@ __all__ = [
'displaytimer', 'displaytimer',
'displaytime', 'displaytime',
'DisplayTimer', 'DisplayTimer',
'AppIntent',
'AppIntentDefault',
'AppIntentExec',
'AppMode',
] ]

View File

@ -9,6 +9,7 @@ from typing import TYPE_CHECKING
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from functools import cached_property from functools import cached_property
from efro.call import tpartial
import _babase import _babase
from babase._language import LanguageSubsystem from babase._language import LanguageSubsystem
from babase._plugin import PluginSubsystem from babase._plugin import PluginSubsystem
@ -16,22 +17,28 @@ from babase._meta import MetadataSubsystem
from babase._net import NetworkSubsystem from babase._net import NetworkSubsystem
from babase._workspace import WorkspaceSubsystem from babase._workspace import WorkspaceSubsystem
from babase._appcomponent import AppComponentSubsystem from babase._appcomponent import AppComponentSubsystem
from babase._appmodeselector import AppModeSelector
from babase._appintent import AppIntentDefault, AppIntentExec
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Any
import asyncio import asyncio
from typing import Any, Callable
from concurrent.futures import Future
from efro.log import LogHandler from efro.log import LogHandler
import babase import babase
from babase._cloud import CloudSubsystem from babase._cloud import CloudSubsystem
from babase._accountv2 import AccountV2Subsystem from babase._accountv2 import AccountV2Subsystem
from babase._apputils import AppHealthMonitor from babase._apputils import AppHealthMonitor
from babase._appintent import AppIntent
from babase._appmode import AppMode
# WOULD-AUTOGEN-BEGIN
# Would autogen this begin
from baclassic import ClassicSubsystem from baclassic import ClassicSubsystem
from baplus import PlusSubsystem from baplus import PlusSubsystem
# Would autogen this end # WOULD-AUTOGEN-END
class App: class App:
@ -275,7 +282,15 @@ class App:
self.lang = LanguageSubsystem() self.lang = LanguageSubsystem()
self.net = NetworkSubsystem() self.net = NetworkSubsystem()
self.workspaces = WorkspaceSubsystem() self.workspaces = WorkspaceSubsystem()
# self._classic: ClassicSubsystem | None = None self._pending_intent: AppIntent | None = None
self._intent: AppIntent | None = None
self._mode: AppMode | None = None
# Controls which app-modes we use for handling given app-intents.
# Plugins can override this to change high level app behavior and
# spinoff projects can change the default implementation for the
# same effect.
self.mode_selector: AppModeSelector | None = None
self._asyncio_timer: babase.AppTimer | None = None self._asyncio_timer: babase.AppTimer | None = None
@ -298,11 +313,28 @@ class App:
# if classic_subsystem_type is not None: # if classic_subsystem_type is not None:
# self._classic = classic_subsystem_type() # self._classic = classic_subsystem_type()
# Would autogen this begin def _threadpool_no_wait_done(self, fut: Future) -> None:
try:
fut.result()
except Exception:
logging.exception(
'Error in work submitted via threadpool_submit_no_wait()'
)
def threadpool_submit_no_wait(self, call: Callable[[], Any]) -> None:
"""Submit work to our threadpool and log any errors.
Use this when you want to run something asynchronously but don't
intend to call result() on it to handle errors/etc.
"""
fut = self.threadpool.submit(call)
fut.add_done_callback(self._threadpool_no_wait_done)
# WOULD-AUTOGEN-BEGIN
@cached_property @cached_property
def classic(self) -> ClassicSubsystem | None: def classic(self) -> ClassicSubsystem | None:
"""Our classic subsystem.""" """Our classic subsystem (if available)."""
try: try:
from baclassic import ClassicSubsystem from baclassic import ClassicSubsystem
@ -316,7 +348,7 @@ class App:
@cached_property @cached_property
def plus(self) -> PlusSubsystem | None: def plus(self) -> PlusSubsystem | None:
"""Our plus subsystem.""" """Our plus subsystem (if available)."""
try: try:
from baplus import PlusSubsystem from baplus import PlusSubsystem
@ -328,7 +360,96 @@ class App:
logging.exception('Error importing baplus') logging.exception('Error importing baplus')
return None return None
# Would autogen this begin # WOULD-AUTOGEN-END
def set_intent(self, intent: AppIntent) -> None:
"""Set the intent for the app.
Intent defines what the app is trying to do at a given time.
This call is asynchronous; the intent switch will happen in the
logic thread in the near future. If set_intent is
called repeatedly before the change takes place, the last intent
set will be used.
"""
# Mark this one as pending. We do this synchronously so that the
# last one marked actually takes effect if there is overlap
# (doing this in the bg thread could result in race conditions).
self._pending_intent = intent
# Do the actual work of calcing our app-mode/etc. in a bg thread
# since it may block for a moment to load modules/etc.
self.threadpool_submit_no_wait(tpartial(self._set_intent, intent))
def _set_intent(self, intent: AppIntent) -> None:
# This should be running in a bg thread.
assert not _babase.in_logic_thread()
try:
# Ask the selector what app-mode to use for this intent.
if self.mode_selector is None:
raise RuntimeError('No AppModeSelector set.')
modetype = self.mode_selector.app_mode_for_intent(intent)
# Make sure the app-mode they return *actually* supports the
# intent.
if not modetype.supports_intent(intent):
raise RuntimeError(
f'Intent {intent} is not supported by AppMode class'
f' {modetype}'
)
# Kick back to the logic thread to apply.
mode = modetype()
_babase.pushcall(
tpartial(self._apply_intent, intent, mode),
from_other_thread=True,
)
except Exception:
logging.exception('Error setting app intent to %s.', intent)
_babase.pushcall(
tpartial(self._apply_intent_error, intent),
from_other_thread=True,
)
def _apply_intent(self, intent: AppIntent, mode: AppMode) -> None:
assert _babase.in_logic_thread()
# ONLY apply this intent if it is still the most recent one
# submitted.
if intent is not self._pending_intent:
return
# If the app-mode for this intent is different than the active
# one, switch.
# pylint: disable=unidiomatic-typecheck
if type(mode) is not type(self._mode):
if self._mode is not None:
try:
self._mode.on_deactivate()
except Exception:
logging.exception(
'Error deactivating app-mode %s.', self._mode
)
self._mode = mode
try:
mode.on_activate()
except Exception:
# Hmm; what should we do in this case?...
logging.exception('Error activating app-mode %s.', mode)
try:
mode.handle_intent(intent)
except Exception:
logging.exception(
'Error handling intent %s in app-mode %s.', intent, mode
)
def _apply_intent_error(self, intent: AppIntent) -> None:
from babase._language import Lstr
del intent # Unused.
_babase.screenmessage(Lstr(resource='errorText'), color=(1, 0, 0))
_babase.getsimplesound('error').play()
def run(self) -> None: def run(self) -> None:
"""Run the app to completion. """Run the app to completion.
@ -381,12 +502,40 @@ class App:
def on_app_loading(self) -> None: def on_app_loading(self) -> None:
"""Called when initially entering the loading state.""" """Called when initially entering the loading state."""
assert _babase.in_logic_thread()
class DefaultAppModeSelector(AppModeSelector):
"""Decides which app modes to use to handle intents."""
def app_mode_for_intent(self, intent: AppIntent) -> type[AppMode]:
# WOULD-AUTOGEN-BEGIN
import bascenev1
return bascenev1.SceneV1AppMode
# WOULD-AUTOGEN-END
def on_app_running(self) -> None: def on_app_running(self) -> None:
"""Called when initially entering the running state.""" """Called when initially entering the running state."""
assert _babase.in_logic_thread()
# Set a default app-mode-selector. Plugins can then override
# this if they want.
self.mode_selector = self.DefaultAppModeSelector()
self.plugins.on_app_running() self.plugins.on_app_running()
# If 'exec' code was provided to the app, always kick that off
# here.
exec_cmd = _babase.exec_arg()
if exec_cmd is not None:
self.set_intent(AppIntentExec(exec_cmd))
elif self._pending_intent is None:
# Otherwise tell the app to do its default thing *only* if a
# plugin hasn't already told it to do something.
self.set_intent(AppIntentDefault())
def on_app_bootstrapping_complete(self) -> None: def on_app_bootstrapping_complete(self) -> None:
"""Called by the C++ layer once its ready to rock.""" """Called by the C++ layer once its ready to rock."""
assert _babase.in_logic_thread() assert _babase.in_logic_thread()

View File

@ -19,19 +19,21 @@ class AppComponentSubsystem:
Category: **App Classes** Category: **App Classes**
This subsystem acts as a registry for classes providing particular This subsystem acts as a registry for classes providing particular
functionality for the app, and allows plugins or other custom code to functionality for the app, and allows plugins or other custom code
easily override said functionality. to easily override said functionality.
Use babase.app.components to get the single shared instance of this class. Access the single shared instance of this class at
babase.app.components.
The general idea with this setup is that a base-class is defined to The general idea with this setup is that a base-class Foo is defined
provide some functionality and then anyone wanting that functionality to provide some functionality and then anyone wanting that
uses the getclass() method with that base class to return the current functionality calls getclass(Foo) to return the current registered
registered implementation. The user should not know or care whether implementation. The user should not know or care whether they are
they are getting the base class itself or some other implementation. getting Foo itself or some subclass of it.
Change-callbacks can also be requested for base classes which will Change-callbacks can also be requested for base classes which will
fire in a deferred manner when particular base-classes are overridden. fire in a deferred manner when particular base-classes are
overridden.
""" """
def __init__(self) -> None: def __init__(self) -> None:
@ -45,8 +47,9 @@ class AppComponentSubsystem:
The provided implementation class must be a subclass of baseclass. The provided implementation class must be a subclass of baseclass.
""" """
# Currently limiting this to logic-thread use; can revisit if needed # Currently limiting this to logic-thread use; can revisit if
# (would need to guard access to our implementations dict). # needed (would need to guard access to our implementations
# dict).
assert _babase.in_logic_thread() assert _babase.in_logic_thread()
if not issubclass(implementation, baseclass): if not issubclass(implementation, baseclass):
@ -58,16 +61,17 @@ class AppComponentSubsystem:
self._implementations[baseclass] = implementation self._implementations[baseclass] = implementation
# If we're the first thing getting dirtied, set up a callback to # If we're the first thing getting dirtied, set up a callback to
# clean everything. And add ourself to the dirty list regardless. # clean everything. And add ourself to the dirty list
# regardless.
if not self._dirty_base_classes: if not self._dirty_base_classes:
_babase.pushcall(self._run_change_callbacks) _babase.pushcall(self._run_change_callbacks)
self._dirty_base_classes.add(baseclass) self._dirty_base_classes.add(baseclass)
def getclass(self, baseclass: T) -> T: def getclass(self, baseclass: T) -> T:
"""Given a base-class, return the currently set implementation class. """Given a base-class, return the current implementation class.
If no custom implementation has been set, the provided base-class If no custom implementation has been set, the provided
is returned. base-class is returned.
""" """
assert _babase.in_logic_thread() assert _babase.in_logic_thread()
@ -77,7 +81,7 @@ class AppComponentSubsystem:
def register_change_callback( def register_change_callback(
self, baseclass: T, callback: Callable[[T], None] self, baseclass: T, callback: Callable[[T], None]
) -> None: ) -> None:
"""Register a callback to fire when a class implementation changes. """Register a callback to fire on class implementation changes.
The callback will be scheduled to run in the logic thread event The callback will be scheduled to run in the logic thread event
loop. Note that any further setclass calls before the callback loop. Note that any further setclass calls before the callback

View File

@ -0,0 +1,27 @@
# Released under the MIT License. See LICENSE for details.
#
"""Provides AppIntent functionality."""
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
pass
class AppIntent:
"""A high level directive given to the app.
Category: **App Classes**
"""
class AppIntentDefault(AppIntent):
"""Tells the app to simply run in its default mode."""
class AppIntentExec(AppIntent):
"""Tells the app to exec some Python code."""
def __init__(self, code: str):
self.code = code

View File

@ -0,0 +1,35 @@
# Released under the MIT License. See LICENSE for details.
#
"""Provides AppMode functionality."""
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from babase._appintent import AppIntent
class AppMode:
"""A high level mode for the app.
Category: **App Classes**
"""
@classmethod
def supports_intent(cls, intent: AppIntent) -> bool:
"""Return whether our mode can handle the provided intent."""
del intent
# Say no to everything by default. Let's make mode explicitly
# lay out everything they *do* support.
return False
def handle_intent(self, intent: AppIntent) -> None:
"""Handle an intent."""
def on_activate(self) -> None:
"""Called when the mode is being activated."""
def on_deactivate(self) -> None:
"""Called when the mode is being deactivated."""

View File

@ -0,0 +1,34 @@
# Released under the MIT License. See LICENSE for details.
#
"""Provides AppMode functionality."""
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from babase._appintent import AppIntent
from babase._appmode import AppMode
class AppModeSelector:
"""Defines which AppModes to use to handle given AppIntents.
Category: **App Classes**
The app calls an instance of this class when passed an AppIntent to
determine which AppMode to use to handle the intent. Plugins or
spinoff projects can modify high level app behavior by replacing or
modifying this.
"""
# pylint: disable=useless-return
def app_mode_for_intent(self, intent: AppIntent) -> type[AppMode]:
"""Given an AppIntent, return the AppMode that should handle it.
If None is returned, the AppIntent will be ignored.
This is called in a background thread, so avoid any calls
limited to logic thread use/etc.
"""
raise RuntimeError('app_mode_for_intent() should be overridden.')

View File

@ -31,7 +31,7 @@ if TYPE_CHECKING:
# Build number and version of the ballistica binary we expect to be # Build number and version of the ballistica binary we expect to be
# using. # using.
TARGET_BALLISTICA_BUILD = 21022 TARGET_BALLISTICA_BUILD = 21023
TARGET_BALLISTICA_VERSION = '1.7.20' TARGET_BALLISTICA_VERSION = '1.7.20'
_g_env_config: EnvConfig | None = None _g_env_config: EnvConfig | None = None

View File

@ -36,6 +36,8 @@ from _babase import (
displaytimer, displaytimer,
DisplayTimer, DisplayTimer,
) )
from babase._appintent import AppIntent, AppIntentDefault, AppIntentExec
from babase._appmode import AppMode
from babase._error import NotFoundError, NodeNotFoundError, ContextError from babase._error import NotFoundError, NodeNotFoundError, ContextError
from babase._language import Lstr from babase._language import Lstr
from babase._general import ( from babase._general import (
@ -55,7 +57,6 @@ from babase._mgen.enums import (
InputType, InputType,
) )
from _bascenev1 import ( from _bascenev1 import (
get_foreground_host_session, get_foreground_host_session,
get_foreground_host_activity, get_foreground_host_activity,
@ -135,6 +136,7 @@ from _bascenev1 import (
) )
from bascenev1._appmode import SceneV1AppMode
from bascenev1._session import Session from bascenev1._session import Session
from bascenev1._map import Map from bascenev1._map import Map
from bascenev1._coopsession import CoopSession from bascenev1._coopsession import CoopSession
@ -200,11 +202,6 @@ from bascenev1._dependency import (
AssetPackage, AssetPackage,
) )
# if TYPE_CHECKING:
# from babase._app import App
# app: App
__all__ = [ __all__ = [
'app', 'app',
'get_local_active_input_devices_count', 'get_local_active_input_devices_count',
@ -383,6 +380,11 @@ __all__ = [
'DisplayTimer', 'DisplayTimer',
'Time', 'Time',
'BaseTime', 'BaseTime',
'AppIntent',
'AppIntentDefault',
'AppIntentExec',
'AppMode',
'SceneV1AppMode',
] ]
# Sanity check: we want to keep ballistica's dependencies and # Sanity check: we want to keep ballistica's dependencies and

View File

@ -0,0 +1,36 @@
# Released under the MIT License. See LICENSE for details.
#
"""Provides AppMode functionality."""
from __future__ import annotations
from typing import TYPE_CHECKING
from babase import AppMode, AppIntentExec, AppIntentDefault
import _bascenev1
if TYPE_CHECKING:
from babase import AppIntent
class SceneV1AppMode(AppMode):
"""Our app-mode."""
@classmethod
def supports_intent(cls, intent: AppIntent) -> bool:
# We support default and exec intents currently.
return isinstance(intent, AppIntentExec | AppIntentDefault)
def handle_intent(self, intent: AppIntent) -> None:
if isinstance(intent, AppIntentExec):
_bascenev1.handle_app_intent_exec(intent.code)
return
assert isinstance(intent, AppIntentDefault)
_bascenev1.handle_app_intent_default()
def on_activate(self) -> None:
# Let the native layer do its thing.
_bascenev1.app_mode_activate()
def on_deactivate(self) -> None:
# Let the native layer do its thing.
_bascenev1.app_mode_deactivate()

View File

@ -58,6 +58,8 @@ from _babase import (
) )
from _babase import screenmessage from _babase import screenmessage
from babase._appintent import AppIntent, AppIntentDefault, AppIntentExec
from babase._appmode import AppMode
from babase._general import Call, WeakCall, AppTime, DisplayTime from babase._general import Call, WeakCall, AppTime, DisplayTime
from babase._language import Lstr from babase._language import Lstr
from babase._plugin import PotentialPlugin, Plugin from babase._plugin import PotentialPlugin, Plugin
@ -201,6 +203,10 @@ __all__ = [
'displaytimer', 'displaytimer',
'DisplayTimer', 'DisplayTimer',
'uibounds', 'uibounds',
'AppIntent',
'AppIntentDefault',
'AppIntentExec',
'AppMode',
] ]
# Sanity check: we want to keep ballistica's dependencies and # Sanity check: we want to keep ballistica's dependencies and

View File

@ -1326,7 +1326,7 @@ void Assets::SetLanguageKeys(
} }
// Let some subsystems know that language has changed. // Let some subsystems know that language has changed.
g_base->app_mode->LanguageChanged(); g_base->app_mode()->LanguageChanged();
g_base->ui->LanguageChanged(); g_base->ui->LanguageChanged();
g_base->graphics->LanguageChanged(); g_base->graphics->LanguageChanged();
} }

View File

@ -71,8 +71,8 @@ BaseFeatureSet::BaseFeatureSet()
text_graphics{new TextGraphics()}, text_graphics{new TextGraphics()},
audio_server{new AudioServer()}, audio_server{new AudioServer()},
assets{new Assets()}, assets{new Assets()},
app_mode{TempSV1CreateAppMode()}, // app_mode{TempSV1CreateAppMode()},
// app_mode{AppModeEmpty::GetSingleton()}, app_mode_{AppModeEmpty::GetSingleton()},
stdio_console{g_buildconfig.enable_stdio_console() ? new StdioConsole() stdio_console{g_buildconfig.enable_stdio_console() ? new StdioConsole()
: nullptr} { : nullptr} {
// We're a singleton. If there's already one of us, something's wrong. // We're a singleton. If there's already one of us, something's wrong.
@ -202,6 +202,11 @@ void BaseFeatureSet::StartApp() {
g_core->BootLog("start-app end"); g_core->BootLog("start-app end");
} }
void BaseFeatureSet::set_app_mode(AppMode* mode) {
assert(InLogicThread());
app_mode_ = mode;
}
auto BaseFeatureSet::AppManagesEventLoop() -> bool { auto BaseFeatureSet::AppManagesEventLoop() -> bool {
return app->ManagesEventLoop(); return app->ManagesEventLoop();
} }

View File

@ -702,9 +702,11 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
UI* const ui; UI* const ui;
Utils* const utils; Utils* const utils;
auto* console() const { return console_; }
auto* app_mode() const { return app_mode_; }
void set_app_mode(AppMode* mode);
// Non-const bits (fixme: clean up access to these). // Non-const bits (fixme: clean up access to these).
AppMode* app_mode;
auto* console() { return console_; }
TouchInput* touch_input{}; TouchInput* touch_input{};
private: private:
@ -713,6 +715,7 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
void PrintContextForCallableLabel(const char* label); void PrintContextForCallableLabel(const char* label);
void PrintContextUnavailable(); void PrintContextUnavailable();
AppMode* app_mode_;
Console* console_{}; Console* console_{};
std::string console_startup_messages_; std::string console_startup_messages_;
bool called_start_app_{}; bool called_start_app_{};

View File

@ -416,7 +416,7 @@ void Graphics::DrawMiscOverlays(RenderPass* pass) {
} }
if (show_ping_) { if (show_ping_) {
std::string ping_str = g_base->app_mode->GetPingString(); std::string ping_str = g_base->app_mode()->GetPingString();
float ping{}; float ping{};
if (!ping_str.empty()) { if (!ping_str.empty()) {
if (ping_str != ping_string_) { if (ping_str != ping_string_) {
@ -452,7 +452,7 @@ void Graphics::DrawMiscOverlays(RenderPass* pass) {
} }
if (show_net_info_) { if (show_net_info_) {
auto net_info_str{g_base->app_mode->GetNetworkDebugString()}; auto net_info_str{g_base->app_mode()->GetNetworkDebugString()};
if (!net_info_str.empty()) { if (!net_info_str.empty()) {
if (net_info_str != net_info_string_) { if (net_info_str != net_info_string_) {
net_info_string_ = net_info_str; net_info_string_ = net_info_str;
@ -908,6 +908,7 @@ void Graphics::AddScreenMessage(const std::string& msg, const Vector3f& color,
} }
void Graphics::Reset() { void Graphics::Reset() {
assert(g_base->InLogicThread());
fade_ = 0; fade_ = 0;
fade_start_ = 0; fade_start_ = 0;
@ -1075,7 +1076,7 @@ void Graphics::DrawWorld(FrameDef* frame_def) {
// Draw all session contents (nodes, etc.) // Draw all session contents (nodes, etc.)
overlay_node_z_depth_ = -0.95f; overlay_node_z_depth_ = -0.95f;
g_base->app_mode->DrawWorld(frame_def); g_base->app_mode()->DrawWorld(frame_def);
g_base->bg_dynamics->Draw(frame_def); g_base->bg_dynamics->Draw(frame_def);
// Lastly draw any blotches that have been building up. // Lastly draw any blotches that have been building up.
@ -1131,7 +1132,7 @@ void Graphics::BuildAndPushFrameDef() {
// wants to know. // wants to know.
if (last_frame_def_graphics_quality_ != frame_def->quality()) { if (last_frame_def_graphics_quality_ != frame_def->quality()) {
last_frame_def_graphics_quality_ = frame_def->quality(); last_frame_def_graphics_quality_ = frame_def->quality();
g_base->app_mode->GraphicsQualityChanged(frame_def->quality()); g_base->app_mode()->GraphicsQualityChanged(frame_def->quality());
} }
ApplyCamera(frame_def); ApplyCamera(frame_def);
@ -1142,7 +1143,7 @@ void Graphics::BuildAndPushFrameDef() {
} else { } else {
// Ok, we're drawing a real frame. // Ok, we're drawing a real frame.
bool session_fills_screen = g_base->app_mode->DoesWorldFillScreen(); bool session_fills_screen = g_base->app_mode()->DoesWorldFillScreen();
frame_def->set_needs_clear(!session_fills_screen); frame_def->set_needs_clear(!session_fills_screen);
DrawWorld(frame_def); DrawWorld(frame_def);

View File

@ -995,8 +995,8 @@ void JoystickInput::HandleSDLEvent(const SDL_Event* e) {
} else { } else {
// FIXME: Need a call we can make for this. // FIXME: Need a call we can make for this.
bool do_party_button = false; bool do_party_button = false;
int party_size = g_base->app_mode->GetPartySize(); int party_size = g_base->app_mode()->GetPartySize();
if (party_size > 1 || g_base->app_mode->HasConnectionToHost() if (party_size > 1 || g_base->app_mode()->HasConnectionToHost()
|| g_base->ui->root_ui()->always_draw_party_icon()) { || g_base->ui->root_ui()->always_draw_party_icon()) {
do_party_button = true; do_party_button = true;
} }

View File

@ -266,7 +266,7 @@ void Input::AddInputDevice(InputDevice* device, bool standard_message) {
// Let the current app-mode assign it a delegate. // Let the current app-mode assign it a delegate.
auto delegate = Object::CompleteDeferred( auto delegate = Object::CompleteDeferred(
g_base->app_mode->CreateInputDeviceDelegate(device)); g_base->app_mode()->CreateInputDeviceDelegate(device));
device->set_delegate(delegate); device->set_delegate(delegate);
delegate->set_input_device(device); delegate->set_input_device(device);
@ -980,12 +980,12 @@ void Input::HandleKeyPress(const SDL_Keysym* keysym) {
case SDLK_EQUALS: case SDLK_EQUALS:
case SDLK_PLUS: case SDLK_PLUS:
g_base->app_mode->ChangeGameSpeed(1); g_base->app_mode()->ChangeGameSpeed(1);
handled = true; handled = true;
break; break;
case SDLK_MINUS: case SDLK_MINUS:
g_base->app_mode->ChangeGameSpeed(-1); g_base->app_mode()->ChangeGameSpeed(-1);
handled = true; handled = true;
break; break;

View File

@ -63,7 +63,7 @@ void Logic::OnAppStart() {
g_base->input->OnAppStart(); g_base->input->OnAppStart();
g_base->ui->OnAppStart(); g_base->ui->OnAppStart();
g_core->platform->OnAppStart(); g_core->platform->OnAppStart();
g_base->app_mode->OnAppStart(); g_base->app_mode()->OnAppStart();
if (g_base->HavePlus()) { if (g_base->HavePlus()) {
g_base->Plus()->OnAppStart(); g_base->Plus()->OnAppStart();
} }
@ -91,7 +91,7 @@ void Logic::OnAppPause() {
if (g_base->HavePlus()) { if (g_base->HavePlus()) {
g_base->Plus()->OnAppPause(); g_base->Plus()->OnAppPause();
} }
g_base->app_mode->OnAppPause(); g_base->app_mode()->OnAppPause();
g_core->platform->OnAppPause(); g_core->platform->OnAppPause();
g_base->ui->OnAppPause(); g_base->ui->OnAppPause();
g_base->input->OnAppPause(); g_base->input->OnAppPause();
@ -108,7 +108,7 @@ void Logic::OnAppResume() {
g_base->input->OnAppResume(); g_base->input->OnAppResume();
g_base->ui->OnAppResume(); g_base->ui->OnAppResume();
g_core->platform->OnAppResume(); g_core->platform->OnAppResume();
g_base->app_mode->OnAppResume(); g_base->app_mode()->OnAppResume();
if (g_base->HavePlus()) { if (g_base->HavePlus()) {
g_base->Plus()->OnAppResume(); g_base->Plus()->OnAppResume();
} }
@ -127,7 +127,7 @@ void Logic::OnAppShutdown() {
if (g_base->HavePlus()) { if (g_base->HavePlus()) {
g_base->Plus()->OnAppShutdown(); g_base->Plus()->OnAppShutdown();
} }
g_base->app_mode->OnAppShutdown(); g_base->app_mode()->OnAppShutdown();
g_core->platform->OnAppResume(); g_core->platform->OnAppResume();
g_base->ui->OnAppShutdown(); g_base->ui->OnAppShutdown();
g_base->input->OnAppShutdown(); g_base->input->OnAppShutdown();
@ -152,7 +152,7 @@ void Logic::ApplyAppConfig() {
g_base->input->ApplyAppConfig(); g_base->input->ApplyAppConfig();
g_base->ui->ApplyAppConfig(); g_base->ui->ApplyAppConfig();
g_core->platform->ApplyAppConfig(); g_core->platform->ApplyAppConfig();
g_base->app_mode->ApplyAppConfig(); g_base->app_mode()->ApplyAppConfig();
if (g_base->HavePlus()) { if (g_base->HavePlus()) {
g_base->Plus()->ApplyAppConfig(); g_base->Plus()->ApplyAppConfig();
} }
@ -203,7 +203,7 @@ void Logic::OnInitialScreenCreated() {
1000 / 10, true, NewLambdaRunnable([this] { StepDisplayTime(); })); 1000 / 10, true, NewLambdaRunnable([this] { StepDisplayTime(); }));
} }
// Let our initial app-mode know it has become active. // Let our initial app-mode know it has become active.
g_base->app_mode->OnActivate(); g_base->app_mode()->OnActivate();
// Let the Python layer know what's up. It will probably flip to // Let the Python layer know what's up. It will probably flip to
// 'Launching' state. // 'Launching' state.
@ -224,6 +224,13 @@ void Logic::CompleteAppBootstrapping() {
g_core->BootLog("app bootstrapping complete"); g_core->BootLog("app bootstrapping complete");
// Reset our various subsystems to a default state.
g_base->ui->Reset();
g_base->input->Reset();
g_base->graphics->Reset();
g_base->python->Reset();
g_base->audio->Reset();
// Let Python know we're done bootstrapping so it can flip the app // Let Python know we're done bootstrapping so it can flip the app
// into the 'launching' state. // into the 'launching' state.
g_base->python->objs() g_base->python->objs()
@ -232,7 +239,7 @@ void Logic::CompleteAppBootstrapping() {
app_bootstrapping_complete_ = true; app_bootstrapping_complete_ = true;
// TODO(ericf): update this for the shiny new app-mode world. // TODO(ericf): update this for the shiny new app-mode world.
if (explicit_bool(true)) { if (explicit_bool(false)) {
// If we were passed launch command args, run them. // If we were passed launch command args, run them.
if (g_core->core_config().exec_command.has_value()) { if (g_core->core_config().exec_command.has_value()) {
bool success = PythonCommand(*g_core->core_config().exec_command, bool success = PythonCommand(*g_core->core_config().exec_command,
@ -248,6 +255,8 @@ void Logic::CompleteAppBootstrapping() {
if (!appmode->GetForegroundSession()) { if (!appmode->GetForegroundSession()) {
appmode->RunMainMenu(); appmode->RunMainMenu();
} }
} else {
// Reset various subsystems
} }
UpdatePendingWorkTimer(); UpdatePendingWorkTimer();
@ -267,7 +276,7 @@ void Logic::OnScreenSizeChange(float virtual_width, float virtual_height,
g_base->input->OnScreenSizeChange(); g_base->input->OnScreenSizeChange();
g_base->ui->OnScreenSizeChange(); g_base->ui->OnScreenSizeChange();
g_core->platform->OnScreenSizeChange(); g_core->platform->OnScreenSizeChange();
g_base->app_mode->OnScreenSizeChange(); g_base->app_mode()->OnScreenSizeChange();
if (g_base->HavePlus()) { if (g_base->HavePlus()) {
g_base->Plus()->OnScreenSizeChange(); g_base->Plus()->OnScreenSizeChange();
} }
@ -287,7 +296,7 @@ void Logic::StepDisplayTime() {
g_base->input->StepDisplayTime(); g_base->input->StepDisplayTime();
g_base->ui->StepDisplayTime(); g_base->ui->StepDisplayTime();
g_core->platform->StepDisplayTime(); g_core->platform->StepDisplayTime();
g_base->app_mode->StepDisplayTime(); g_base->app_mode()->StepDisplayTime();
if (g_base->HavePlus()) { if (g_base->HavePlus()) {
g_base->Plus()->StepDisplayTime(); g_base->Plus()->StepDisplayTime();
} }

View File

@ -234,7 +234,7 @@ auto NetworkReader::RunThread() -> int {
memcpy(s_buffer.data(), buffer + 1, rresult2 - 1); memcpy(s_buffer.data(), buffer + 1, rresult2 - 1);
s_buffer[rresult2 - 1] = 0; // terminate string s_buffer[rresult2 - 1] = 0; // terminate string
std::string response = std::string response =
g_base->app_mode->HandleJSONPing(s_buffer.data()); g_base->app_mode()->HandleJSONPing(s_buffer.data());
if (!response.empty()) { if (!response.empty()) {
std::vector<char> msg(1 + response.size()); std::vector<char> msg(1 + response.size());
msg[0] = BA_PACKET_JSON_PONG; msg[0] = BA_PACKET_JSON_PONG;
@ -302,7 +302,7 @@ auto NetworkReader::RunThread() -> int {
} }
case BA_PACKET_HOST_QUERY: { case BA_PACKET_HOST_QUERY: {
g_base->app_mode->HandleGameQuery(buffer, rresult2, &from); g_base->app_mode()->HandleGameQuery(buffer, rresult2, &from);
// HandleGameQuery(buffer, rresult2, &from); // HandleGameQuery(buffer, rresult2, &from);
break; break;
@ -338,8 +338,9 @@ void NetworkReader::PushIncomingUDPPacketCall(const std::vector<uint8_t>& data,
return; return;
} }
g_base->logic->event_loop()->PushCall( g_base->logic->event_loop()->PushCall([data, addr] {
[data, addr] { g_base->app_mode->HandleIncomingUDPPacket(data, addr); }); g_base->app_mode()->HandleIncomingUDPPacket(data, addr);
});
} }
void NetworkReader::OpenSockets() { void NetworkReader::OpenSockets() {

View File

@ -239,7 +239,7 @@ static auto PyPushCall(PyObject* self, PyObject* args, PyObject* keywds)
// Run this with an empty context by default, or foreground if // Run this with an empty context by default, or foreground if
// requested. // requested.
ScopedSetContext ssc(other_thread_use_fg_context ScopedSetContext ssc(other_thread_use_fg_context
? g_base->app_mode->GetForegroundContext() ? g_base->app_mode()->GetForegroundContext()
: ContextRef(nullptr)); : ContextRef(nullptr));
PythonRef(call_obj, PythonRef::kSteal).Call(); PythonRef(call_obj, PythonRef::kSteal).Call();
@ -1243,6 +1243,28 @@ static PyMethodDef PyIsOSPlayingMusicDef = {
"(Used to determine whether the game should avoid playing its own)", "(Used to determine whether the game should avoid playing its own)",
}; };
// -------------------------------- exec_arg -----------------------------------
static auto PyExecArg(PyObject* self) -> PyObject* {
BA_PYTHON_TRY;
if (g_core->core_config().exec_command.has_value()) {
return PyUnicode_FromString(g_core->core_config().exec_command->c_str());
}
Py_RETURN_NONE;
BA_PYTHON_CATCH;
}
static PyMethodDef PyExecArgDef = {
"exec_arg", // name
(PyCFunction)PyExecArg, // method
METH_NOARGS, // flags
"exec_arg() -> str | None\n"
"\n"
"(internal)\n",
};
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
auto PythonMethodsApp::GetMethods() -> std::vector<PyMethodDef> { auto PythonMethodsApp::GetMethods() -> std::vector<PyMethodDef> {
@ -1281,6 +1303,7 @@ auto PythonMethodsApp::GetMethods() -> std::vector<PyMethodDef> {
PyMacMusicAppGetPlaylistsDef, PyMacMusicAppGetPlaylistsDef,
PyIsOSPlayingMusicDef, PyIsOSPlayingMusicDef,
PyBootLogDef, PyBootLogDef,
PyExecArgDef,
}; };
} }

View File

@ -0,0 +1,18 @@
// Released under the MIT License. See LICENSE for details.
#ifndef BALLISTICA_BASE_SUPPORT_CLASSIC_SOFT_H_
#define BALLISTICA_BASE_SUPPORT_CLASSIC_SOFT_H_
namespace ballistica::base {
/// 'Soft' interface to the classic feature-set.
/// Feature-sets listing classic as a soft requirement must limit their use of
/// it to these methods and should be prepared to handle the not-present
/// case.
class ClassicSoftInterface {
public:
};
} // namespace ballistica::base
#endif // BALLISTICA_BASE_SUPPORT_CLASSIC_SOFT_H_

View File

@ -107,7 +107,7 @@ void StdioConsole::OnMainThreadStartApp() {
void StdioConsole::PushCommand(const std::string& command) { void StdioConsole::PushCommand(const std::string& command) {
g_base->logic->event_loop()->PushCall([command] { g_base->logic->event_loop()->PushCall([command] {
// These are always run in whichever context is 'visible'. // These are always run in whichever context is 'visible'.
ScopedSetContext ssc(g_base->app_mode->GetForegroundContext()); ScopedSetContext ssc(g_base->app_mode()->GetForegroundContext());
PythonCommand cmd(command, "<stdin>"); PythonCommand cmd(command, "<stdin>");
if (!g_core->user_ran_commands) { if (!g_core->user_ran_commands) {
g_core->user_ran_commands = true; g_core->user_ran_commands = true;

View File

@ -146,7 +146,7 @@ void Console::PushCommand(const std::string& command) {
assert(g_base); assert(g_base);
g_base->logic->event_loop()->PushCall([command] { g_base->logic->event_loop()->PushCall([command] {
// These are always run in whichever context is 'visible'. // These are always run in whichever context is 'visible'.
ScopedSetContext ssc(g_base->app_mode->GetForegroundContext()); ScopedSetContext ssc(g_base->app_mode()->GetForegroundContext());
PythonCommand cmd(command, "<console>"); PythonCommand cmd(command, "<console>");
if (!g_core->user_ran_commands) { if (!g_core->user_ran_commands) {
g_core->user_ran_commands = true; g_core->user_ran_commands = true;

View File

@ -660,7 +660,7 @@ static PyMethodDef PyEndHostScanningDef = {
static auto PyHaveConnectedClients(PyObject* self, PyObject* args, static auto PyHaveConnectedClients(PyObject* self, PyObject* args,
PyObject* keywds) -> PyObject* { PyObject* keywds) -> PyObject* {
BA_PYTHON_TRY; BA_PYTHON_TRY;
if (g_base->app_mode->HasConnectionToClients()) { if (g_base->app_mode()->HasConnectionToClients()) {
Py_RETURN_TRUE; Py_RETURN_TRUE;
} else { } else {
Py_RETURN_FALSE; Py_RETURN_FALSE;

View File

@ -30,6 +30,7 @@
#include "ballistica/scene_v1/support/session_stream.h" #include "ballistica/scene_v1/support/session_stream.h"
#include "ballistica/shared/generic/json.h" #include "ballistica/shared/generic/json.h"
#include "ballistica/shared/generic/utils.h" #include "ballistica/shared/generic/utils.h"
#include "ballistica/shared/python/python_command.h"
namespace ballistica::scene_v1 { namespace ballistica::scene_v1 {
@ -1611,6 +1612,109 @@ static PyMethodDef PySetInternalMusicDef = {
"(internal).", "(internal).",
}; };
// --------------------------- app_mode_activate -------------------------------
static auto PyAppModeActivate(PyObject* self) -> PyObject* {
BA_PYTHON_TRY;
BA_PRECONDITION(g_base->InLogicThread());
g_base->set_app_mode(SceneV1AppMode::GetSingleton());
Py_RETURN_NONE;
BA_PYTHON_CATCH;
}
static PyMethodDef PyAppModeActivateDef = {
"app_mode_activate", // name
(PyCFunction)PyAppModeActivate, // method
METH_NOARGS, // flags
"app_mode_activate() -> None\n"
"\n"
"(internal)\n",
};
// -------------------------- app_mode_deactivate ------------------------------
static auto PyAppModeDeactivate(PyObject* self) -> PyObject* {
BA_PYTHON_TRY;
BA_PRECONDITION(g_base->InLogicThread());
// Currently doing nothing.
Py_RETURN_NONE;
BA_PYTHON_CATCH;
}
static PyMethodDef PyAppModeDeactivateDef = {
"app_mode_deactivate", // name
(PyCFunction)PyAppModeDeactivate, // method
METH_NOARGS, // flags
"app_mode_deactivate() -> None\n"
"\n"
"(internal)\n",
};
// ----------------------- handle_app_intent_default ---------------------------
static auto PyHandleAppIntentDefault(PyObject* self) -> PyObject* {
BA_PYTHON_TRY;
BA_PRECONDITION(g_base->InLogicThread());
auto* appmode = SceneV1AppMode::GetActiveOrThrow();
appmode->RunMainMenu();
Py_RETURN_NONE;
BA_PYTHON_CATCH;
}
static PyMethodDef PyHandleAppIntentDefaultDef = {
"handle_app_intent_default", // name
(PyCFunction)PyHandleAppIntentDefault, // method
METH_NOARGS, // flags
"handle_app_intent_default() -> None\n"
"\n"
"(internal)\n",
};
// ------------------------ handle_app_intent_exec -----------------------------
static auto PyHandleAppIntentExec(PyObject* self, PyObject* args,
PyObject* keywds) -> PyObject* {
BA_PYTHON_TRY;
const char* command;
static const char* kwlist[] = {"command", nullptr};
if (!PyArg_ParseTupleAndKeywords(args, keywds, "s",
const_cast<char**>(kwlist), &command)) {
return nullptr;
}
auto* appmode = SceneV1AppMode::GetActiveOrThrow();
// Run the command.
if (g_core->core_config().exec_command.has_value()) {
bool success = PythonCommand(*g_core->core_config().exec_command,
BA_BUILD_COMMAND_FILENAME)
.Exec(true, nullptr, nullptr);
if (!success) {
// FIXME: what should we do in this case?
// exit(1);
}
}
// If the stuff we just ran didn't result in a session, create a default
// one.
if (!appmode->GetForegroundSession()) {
appmode->RunMainMenu();
}
Py_RETURN_NONE;
BA_PYTHON_CATCH;
}
static PyMethodDef PyHandleAppIntentExecDef = {
"handle_app_intent_exec", // name
(PyCFunction)PyHandleAppIntentExec, // method
METH_VARARGS | METH_KEYWORDS, // flags
"handle_app_intent_exec(command: str) -> None\n"
"\n"
"(internal)",
};
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
auto PythonMethodsScene::GetMethods() -> std::vector<PyMethodDef> { auto PythonMethodsScene::GetMethods() -> std::vector<PyMethodDef> {
@ -1646,6 +1750,10 @@ auto PythonMethodsScene::GetMethods() -> std::vector<PyMethodDef> {
PyBaseTimeDef, PyBaseTimeDef,
PyBaseTimerDef, PyBaseTimerDef,
PyLsInputDevicesDef, PyLsInputDevicesDef,
PyAppModeActivateDef,
PyAppModeDeactivateDef,
PyHandleAppIntentDefaultDef,
PyHandleAppIntentExecDef,
}; };
} }

View File

@ -315,7 +315,7 @@ auto SceneV1AppMode::GetActive() -> SceneV1AppMode* {
// keep in mind that app-mode may change under them. // keep in mind that app-mode may change under them.
// Otherwise return our singleton only if it is current. // Otherwise return our singleton only if it is current.
if (g_base->app_mode == g_scene_v1_app_mode) { if (g_base->app_mode() == g_scene_v1_app_mode) {
return g_scene_v1_app_mode; return g_scene_v1_app_mode;
} }
return nullptr; return nullptr;
@ -1389,7 +1389,7 @@ void SceneV1AppMode::HandleGameQuery(const char* buffer, size_t size,
if (size == 5) { if (size == 5) {
// If we're already in a party, don't advertise since they // If we're already in a party, don't advertise since they
// wouldn't be able to join us anyway. // wouldn't be able to join us anyway.
if (g_base->app_mode->HasConnectionToHost()) { if (g_base->app_mode()->HasConnectionToHost()) {
return; return;
} }

View File

@ -10,7 +10,7 @@
namespace ballistica::scene_v1 { namespace ballistica::scene_v1 {
auto ContextRefSceneV1::FromAppForegroundContext() -> ContextRefSceneV1 { auto ContextRefSceneV1::FromAppForegroundContext() -> ContextRefSceneV1 {
auto* c = g_base->app_mode->GetForegroundContext().Get(); auto* c = g_base->app_mode()->GetForegroundContext().Get();
return ContextRefSceneV1(c); return ContextRefSceneV1(c);
} }

View File

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

View File

@ -2665,8 +2665,8 @@ static auto PyCanShowAd(PyObject* self, PyObject* args, PyObject* keywds)
// them or whatnot). Also disallow ads if remote apps are connected; at least // them or whatnot). Also disallow ads if remote apps are connected; at least
// on Android, ads pause our activity which disconnects the remote app. // on Android, ads pause our activity which disconnects the remote app.
// (need to fix this). // (need to fix this).
if (g_base->app_mode->HasConnectionToHost() if (g_base->app_mode()->HasConnectionToHost()
|| g_base->app_mode->HasConnectionToClients() || g_base->app_mode()->HasConnectionToClients()
|| g_base->input->HaveRemoteAppController()) { || g_base->input->HaveRemoteAppController()) {
Py_RETURN_FALSE; Py_RETURN_FALSE;
} }
@ -2873,8 +2873,8 @@ static auto PyIsPartyIconVisible(PyObject* self, PyObject* args,
PyObject* keywds) -> PyObject* { PyObject* keywds) -> PyObject* {
BA_PYTHON_TRY; BA_PYTHON_TRY;
bool party_button_active = bool party_button_active =
(g_base->app_mode->HasConnectionToClients() (g_base->app_mode()->HasConnectionToClients()
|| g_base->app_mode->HasConnectionToHost() || g_base->app_mode()->HasConnectionToHost()
|| g_base->ui->root_ui()->always_draw_party_icon()); || g_base->ui->root_ui()->always_draw_party_icon());
if (party_button_active) { if (party_button_active) {
Py_RETURN_TRUE; Py_RETURN_TRUE;

View File

@ -42,8 +42,9 @@ RootUI::~RootUI() = default;
void RootUI::TogglePartyWindowKeyPress() { void RootUI::TogglePartyWindowKeyPress() {
assert(g_base->InLogicThread()); assert(g_base->InLogicThread());
if (g_base->app_mode->GetPartySize() > 1 if (g_base->app_mode()->GetPartySize() > 1
|| g_base->app_mode->HasConnectionToHost() || always_draw_party_icon()) { || g_base->app_mode()->HasConnectionToHost()
|| always_draw_party_icon()) {
ActivatePartyIcon(); ActivatePartyIcon();
} }
} }
@ -77,8 +78,8 @@ auto RootUI::HandleMouseButtonDown(float x, float y) -> bool {
// floats over the top). Party button is to the left of menu button. // floats over the top). Party button is to the left of menu button.
if (explicit_bool(DO_OLD_MENU_PARTY_BUTTONS)) { if (explicit_bool(DO_OLD_MENU_PARTY_BUTTONS)) {
bool party_button_active = (!party_window_open_ bool party_button_active = (!party_window_open_
&& (g_base->app_mode->HasConnectionToClients() && (g_base->app_mode()->HasConnectionToClients()
|| g_base->app_mode->HasConnectionToHost() || g_base->app_mode()->HasConnectionToHost()
|| always_draw_party_icon())); || always_draw_party_icon()));
float party_button_left = float party_button_left =
menu_active ? 2 * menu_button_size_ : menu_button_size_; menu_active ? 2 * menu_button_size_ : menu_button_size_;
@ -202,17 +203,17 @@ void RootUI::Draw(base::FrameDef* frame_def) {
// To the left of the menu button, draw our connected-players indicator // To the left of the menu button, draw our connected-players indicator
// (this probably shouldn't live here). // (this probably shouldn't live here).
bool draw_connected_players_icon = false; bool draw_connected_players_icon = false;
int party_size = g_base->app_mode->GetPartySize(); int party_size = g_base->app_mode()->GetPartySize();
bool is_host = (!g_base->app_mode->HasConnectionToHost()); bool is_host = (!g_base->app_mode()->HasConnectionToHost());
millisecs_t last_connection_to_client_join_time = millisecs_t last_connection_to_client_join_time =
g_base->app_mode->LastClientJoinTime(); g_base->app_mode()->LastClientJoinTime();
bool show_client_joined = bool show_client_joined =
(is_host && last_connection_to_client_join_time != 0 (is_host && last_connection_to_client_join_time != 0
&& real_time - last_connection_to_client_join_time < 5000); && real_time - last_connection_to_client_join_time < 5000);
if (!party_window_open_ if (!party_window_open_
&& (party_size != 0 || g_base->app_mode->HasConnectionToHost() && (party_size != 0 || g_base->app_mode()->HasConnectionToHost()
|| always_draw_party_icon_)) { || always_draw_party_icon_)) {
draw_connected_players_icon = true; draw_connected_players_icon = true;
} }
@ -221,7 +222,7 @@ void RootUI::Draw(base::FrameDef* frame_def) {
// Flash and show a message if we're in the main menu instructing the // Flash and show a message if we're in the main menu instructing the
// player to start a game. // player to start a game.
bool flash = false; bool flash = false;
bool in_main_menu = g_base->app_mode->InMainMenu(); bool in_main_menu = g_base->app_mode()->InMainMenu();
if (in_main_menu && party_size > 0 && show_client_joined) flash = true; if (in_main_menu && party_size > 0 && show_client_joined) flash = true;

View File

@ -895,7 +895,7 @@ void RootWidget::UpdateForFocusedWindow() {
void RootWidget::UpdateForFocusedWindow(Widget* widget) { void RootWidget::UpdateForFocusedWindow(Widget* widget) {
// Take note if the current session is the main menu; we do a few things // Take note if the current session is the main menu; we do a few things
// differently there. // differently there.
in_main_menu_ = g_base->app_mode->InMainMenu(); in_main_menu_ = g_base->app_mode()->InMainMenu();
if (widget == nullptr) { if (widget == nullptr) {
toolbar_visibility_ = ToolbarVisibility::kInGame; toolbar_visibility_ = ToolbarVisibility::kInGame;

View File

@ -312,7 +312,10 @@ class ProjectUpdater:
) )
for i, change in enumerate(auto_changes): for i, change in enumerate(auto_changes):
print(f'{Clr.BLU}Correcting file: {change[0]}{Clr.RST}') print(
f'{Clr.BLU}{Clr.BLD}Correcting'
f' {change[0]} line {change[1].line_number+1}{Clr.RST}'
)
with open( with open(
os.path.join(self.projroot, change[0]), encoding='utf-8' os.path.join(self.projroot, change[0]), encoding='utf-8'
) as infile: ) as infile: