mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-01-27 09:23:12 +08:00
Merge branch 'efroemling:master' into master
This commit is contained in:
commit
add619b080
108
.efrocachemap
generated
108
.efrocachemap
generated
@ -421,21 +421,21 @@
|
||||
"build/assets/ba_data/audio/zoeOw.ogg": "74befe45a8417e95b6a2233c51992a26",
|
||||
"build/assets/ba_data/audio/zoePickup01.ogg": "48ab8cddfcde36a750856f3f81dd20c8",
|
||||
"build/assets/ba_data/audio/zoeScream01.ogg": "2b468aedfa8741090247f04eb9e6df55",
|
||||
"build/assets/ba_data/data/langdata.json": "c6f94f9c1dc833c537d16672d9018b94",
|
||||
"build/assets/ba_data/data/langdata.json": "f200cdf431b9494d8b96cdd47e950dd1",
|
||||
"build/assets/ba_data/data/languages/arabic.json": "00ba700de6c672a56658a6bd1ad27523",
|
||||
"build/assets/ba_data/data/languages/belarussian.json": "7fe38341815ca6ff4d95224196e7a67e",
|
||||
"build/assets/ba_data/data/languages/belarussian.json": "40883823367f04c5a2403a96525bfcc3",
|
||||
"build/assets/ba_data/data/languages/chinese.json": "5761468d25f2bd4e79921826cebd572b",
|
||||
"build/assets/ba_data/data/languages/chinesetraditional.json": "f858da49be0a5374157c627857751078",
|
||||
"build/assets/ba_data/data/languages/croatian.json": "766532c67af5bd0144c2d63cab0516fa",
|
||||
"build/assets/ba_data/data/languages/czech.json": "93c5fe0d884d95435da6c675f64e30e0",
|
||||
"build/assets/ba_data/data/languages/czech.json": "cd21ad8c6b8e9ed700284cf1e1aecbf8",
|
||||
"build/assets/ba_data/data/languages/danish.json": "3fd69080783d5c9dcc0af737f02b6f1e",
|
||||
"build/assets/ba_data/data/languages/dutch.json": "22b44a33bf81142ba2befad14eb5746e",
|
||||
"build/assets/ba_data/data/languages/english.json": "bd43b77b1ccca059573acbde148b4767",
|
||||
"build/assets/ba_data/data/languages/english.json": "6a3fab4fb8b2879e00ed9877709bf504",
|
||||
"build/assets/ba_data/data/languages/esperanto.json": "0e397cfa5f3fb8cef5f4a64f21cda880",
|
||||
"build/assets/ba_data/data/languages/filipino.json": "afbda3adf14555e1567ee63c32e340e7",
|
||||
"build/assets/ba_data/data/languages/filipino.json": "6f4051ce78861a4666f4978d6f9a0ed2",
|
||||
"build/assets/ba_data/data/languages/french.json": "49ff6d211537b8003b8241438dca661d",
|
||||
"build/assets/ba_data/data/languages/german.json": "450fa41ae264f29a5d1af22143d0d0ad",
|
||||
"build/assets/ba_data/data/languages/gibberish.json": "9aae526303a22372fe9b4cf1781520ef",
|
||||
"build/assets/ba_data/data/languages/gibberish.json": "25fcb5130fae56985bee175aa19f86a2",
|
||||
"build/assets/ba_data/data/languages/greek.json": "287c0ec437b38772284ef9d3e4fb2fc3",
|
||||
"build/assets/ba_data/data/languages/hindi.json": "8848f6b0caec0fcf9d85bc6e683809ec",
|
||||
"build/assets/ba_data/data/languages/hungarian.json": "796a290a8c44a1e7635208c2ff5fdc6e",
|
||||
@ -445,12 +445,12 @@
|
||||
"build/assets/ba_data/data/languages/malay.json": "832562ce997fc70704b9234c95fb2e38",
|
||||
"build/assets/ba_data/data/languages/persian.json": "d742f4a6d3c3555031102b21abdcbb5b",
|
||||
"build/assets/ba_data/data/languages/polish.json": "b9a58b70ed5e99d8b7fa2392b2eb0cda",
|
||||
"build/assets/ba_data/data/languages/portuguese.json": "556af4e8170356ad239412e1743e20d5",
|
||||
"build/assets/ba_data/data/languages/portuguese.json": "e3adc6c04486d21e84019a0b03ce11b1",
|
||||
"build/assets/ba_data/data/languages/romanian.json": "aeebdd54f65939c2facc6ac50c117826",
|
||||
"build/assets/ba_data/data/languages/russian.json": "e120993371f52edd2d99f2236188933c",
|
||||
"build/assets/ba_data/data/languages/serbian.json": "d7452dd72ac0e51680cb39b5ebaa1c69",
|
||||
"build/assets/ba_data/data/languages/slovak.json": "27962d53dc3f7dd4e877cd40faafeeef",
|
||||
"build/assets/ba_data/data/languages/spanish.json": "80ea58bd3295a0252b7fdac9154aa22f",
|
||||
"build/assets/ba_data/data/languages/spanish.json": "1d14210b4eefb48130608bd0495b7900",
|
||||
"build/assets/ba_data/data/languages/swedish.json": "5142a96597d17d8344be96a603da64ac",
|
||||
"build/assets/ba_data/data/languages/tamil.json": "b4de1a2851afe4869c82e9acd94cd89c",
|
||||
"build/assets/ba_data/data/languages/thai.json": "77755219bbf5fb7eea0d6b226684f403",
|
||||
@ -4060,50 +4060,50 @@
|
||||
"build/assets/windows/Win32/ucrtbased.dll": "2def5335207d41b21b9823f6805997f1",
|
||||
"build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae",
|
||||
"build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599",
|
||||
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "9fe23e06319e4e256b9fa88814a14afa",
|
||||
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "4306acae21ce88235f9d1589086866e7",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "75e4f7d3a3df67dedd079ec3f4441094",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "bd5eda13f239b81886ac80596d6ade73",
|
||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "0805235a92dd91f96d43ea54575eecac",
|
||||
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "07589a61b11cbc5fca0bbc8b7fc1c955",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "f28629761060c8152168b6792b71adae",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "1cfd1a33474cdb31834994f626385ed0",
|
||||
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "d50879a92d9d344c376f6f196d78d1be",
|
||||
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "78cd0edf2698f197f2acd80ca364fae7",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "d656f47118ebc3af57c40423cb258bc8",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "75df540b27779342a7c696e1bdbe593f",
|
||||
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "76f0dfacaa9ea67e45e8ccf3bb3bc1c6",
|
||||
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "2acc754bed825a9265e0621dc09899e0",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "62c2b6190de8784ea8750ea50e6a2304",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "e57358fd9a948a8ce82a54cdd5c766fc",
|
||||
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "d36e3303e13049eae5e7ec19861d300e",
|
||||
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "46971a2ca1e3021e52ea5d0f4938d2ff",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "19ebd36613cf62c4bd50e70b93371368",
|
||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "49ef5905b6e9e1a9caaed3d1c1da4ea5",
|
||||
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "c22901e06e88a55cce0b4e08bbf41a4c",
|
||||
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "a27963487e346338e4c216bd4fbb9e2a",
|
||||
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "c22901e06e88a55cce0b4e08bbf41a4c",
|
||||
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "a27963487e346338e4c216bd4fbb9e2a",
|
||||
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "2663c888aec894656bd8c49932bd7729",
|
||||
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "5e57d12a3cfcfbc47b0293c3cb9fdca9",
|
||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "2663c888aec894656bd8c49932bd7729",
|
||||
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "5e57d12a3cfcfbc47b0293c3cb9fdca9",
|
||||
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "0f7dbe6fb3e28a51904aa822b509da0f",
|
||||
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "081b766945b52460a4f1afc01faa0652",
|
||||
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "0f7dbe6fb3e28a51904aa822b509da0f",
|
||||
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "081b766945b52460a4f1afc01faa0652",
|
||||
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "ad609c63f68417d5211bbfb23ce4affe",
|
||||
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "852fe46c736082611a831a618923c241",
|
||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "36fbda7829ed5c2862c34feb09b03402",
|
||||
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "852fe46c736082611a831a618923c241",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "38b4b5b85a9bafdb76222d0f0c962b06",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "9a8af3d217bcb0bacfaed4c30dd5f42e",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "6d10ca306f60d66efb4942636e4955d6",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "1c65d36e4420ed79380dc8c041c94a8b",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "9b1b72f3d41c89a6b06288be63e8f40a",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "e0f2eb8ea024bc88e999b9dc16317fd4",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "42be1225757328f432d91de950444ba0",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "05bc2832cc0c9fba308668fc1a6d3b0f",
|
||||
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "6135aeb242afaf9d1114810a67c89cec",
|
||||
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "bbbbb14d42ed6eb0c5eb56867b7fb870",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "cd28f9cc4652736a31c677fc4e5dbaf1",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "239c608cc52c0320210e56ad6abe57a5",
|
||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "e76d67cacf1393d33796d6b6b1bf1413",
|
||||
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "a7eaa8dc4d859ef7a735483b04ccec4a",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "7a2eef42da34a35ddcc2fd7c66843b1b",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "694599ac6a967b2ed383b27bf8093e5b",
|
||||
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "c91cbab6a07affa22e0612210f8b807c",
|
||||
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "d460f7a3909f92d5dbf752e4521a9fbc",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "0a0abfe75bc987e7b65a3cfa106e8353",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "8f21405b29f2b2ab01323d711492cca0",
|
||||
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "96dc73e819f41f99a1b2dbb45f79d551",
|
||||
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "c79ac51cd2deabb1c2d0acddeaf81c30",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "f06ec14e8c3106be9df91af7da621dc9",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "f389f9a7b1afc81f76787722340cfa9c",
|
||||
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "c7dab78aac11cb1430d8456d5d48107a",
|
||||
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "67e29852dfee2e63e179cfebf608ef26",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "9778f8faf91c9993fbf3015bd4554a87",
|
||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "73477bd15b9e3834314fd878c9e108d4",
|
||||
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "fb9b8443c1b4cccad749df7d6328220f",
|
||||
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "384fb7fd55ad5a6cdbb662da1ec402ab",
|
||||
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "fb9b8443c1b4cccad749df7d6328220f",
|
||||
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "384fb7fd55ad5a6cdbb662da1ec402ab",
|
||||
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "bc7d0811bcd87156ebf5292a38a1c350",
|
||||
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "bb32f45054b6999300bf8b41d6a4b402",
|
||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "bc7d0811bcd87156ebf5292a38a1c350",
|
||||
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "bb32f45054b6999300bf8b41d6a4b402",
|
||||
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "8d9a1505bf397f4902baabed7c1cf438",
|
||||
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "f4d9c115e22dd81e36d1c5baeac8d848",
|
||||
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "8d9a1505bf397f4902baabed7c1cf438",
|
||||
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "f4d9c115e22dd81e36d1c5baeac8d848",
|
||||
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "fb72c92ec6ec0e1c8f4ced32abd86505",
|
||||
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "131aab20cfe77fe89c3f452a855f1e68",
|
||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "ee10cdc9f9a861e2be0f1a208c0ca0fe",
|
||||
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "131aab20cfe77fe89c3f452a855f1e68",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "678fabc6dfd6f401ee8942d088ee9181",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "e092d2aed8464a61a623d79ca25308d8",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "6b658f49be396ad645c5e57464739a3b",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "9d79a56403a6d806ff131a7de664dfa7",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "e831a26d2c28e862d51e24393d158c99",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "46fe1c89bcc75c781729ec9e5491c610",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "9c6278d7df3ce4db2ffe7794a0fd35b7",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "110c35a17b462864075800756b5e541a",
|
||||
"src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c",
|
||||
"src/assets/ba_data/python/babase/_mgen/enums.py": "28323912b56ec07701eda3d41a6a4101",
|
||||
"src/ballistica/base/mgen/pyembed/binding_base.inc": "72bfed2cce8ff19741989dec28302f3f",
|
||||
@ -4112,7 +4112,7 @@
|
||||
"src/ballistica/core/mgen/pyembed/binding_core.inc": "9d0a3c9636138e35284923e0c8311c69",
|
||||
"src/ballistica/core/mgen/pyembed/env.inc": "8be46e5818f360d10b7b0224a9e91d07",
|
||||
"src/ballistica/core/mgen/python_modules_monolithic.h": "fb967ed1c7db0c77d8deb4f00a7103c5",
|
||||
"src/ballistica/scene_v1/mgen/pyembed/binding_scene_v1.inc": "d80f970053099b3044204bfe29ddefce",
|
||||
"src/ballistica/scene_v1/mgen/pyembed/binding_scene_v1.inc": "c25b263f2a31fb5ebe057db07d144879",
|
||||
"src/ballistica/template_fs/mgen/pyembed/binding_template_fs.inc": "44a45492db057bf7f7158c3b0fa11f0f",
|
||||
"src/ballistica/ui_v1/mgen/pyembed/binding_ui_v1.inc": "8f4c2070174bdc2fbf735180394d7b3a"
|
||||
"src/ballistica/ui_v1/mgen/pyembed/binding_ui_v1.inc": "f5f054050d2b2fcd3763a4833fb32269"
|
||||
}
|
||||
178
.github/workflows/cd.yml
vendored
Normal file
178
.github/workflows/cd.yml
vendored
Normal file
@ -0,0 +1,178 @@
|
||||
name: CD
|
||||
|
||||
on:
|
||||
# Run on pushes and pull-requests
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
make_linux_x86_64_gui_debug_build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install pip requirements
|
||||
run: tools/pcommand install_pip_reqs
|
||||
- name: Make the build
|
||||
run: make prefab-gui-debug-build
|
||||
- name: Upload the build
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: linux_x86_64_gui_(debug)
|
||||
path: build/prefab/full/linux_x86_64_gui
|
||||
make_linux_x86_64_server_debug_build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install pip requirements
|
||||
run: tools/pcommand install_pip_reqs
|
||||
- name: Make the build
|
||||
run: make prefab-server-debug-build
|
||||
- name: Upload the build
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: linux_x86_64_server_(debug)
|
||||
path: build/prefab/full/linux_x86_64_server
|
||||
make_linux_arm64_gui_debug_build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install pip requirements
|
||||
run: tools/pcommand install_pip_reqs
|
||||
- name: Make the build
|
||||
run: make prefab-linux-arm64-gui-debug-build
|
||||
- name: Upload the build
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: linux_arm64_gui_(debug)
|
||||
path: build/prefab/full/linux_arm64_gui
|
||||
make_linux_arm64_server_debug_build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install pip requirements
|
||||
run: tools/pcommand install_pip_reqs
|
||||
- name: Make the build
|
||||
run: make prefab-linux-arm64-server-debug-build
|
||||
- name: Upload the build
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: linux_arm64_server_(debug)
|
||||
path: build/prefab/full/linux_arm64_server
|
||||
make_mac_x86_64_gui_debug_build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install pip requirements
|
||||
run: tools/pcommand install_pip_reqs
|
||||
- name: Make the build
|
||||
run: make prefab-mac-x86-64-gui-debug-build
|
||||
- name: Upload the build
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: mac_x86_64_gui_(debug)
|
||||
path: build/prefab/full/mac_x86_64_gui
|
||||
make_mac_x86_64_server_debug_build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install pip requirements
|
||||
run: tools/pcommand install_pip_reqs
|
||||
- name: Make the build
|
||||
run: make prefab-mac-x86-64-server-debug-build
|
||||
- name: Upload the build
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: mac_x86_64_server_(debug)
|
||||
path: build/prefab/full/mac_x86_64_server
|
||||
make_mac_arm64_gui_debug_build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install pip requirements
|
||||
run: tools/pcommand install_pip_reqs
|
||||
- name: Make the build
|
||||
run: make prefab-mac-arm64-gui-debug-build
|
||||
- name: Upload the build
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: mac_arm64_gui_(debug)
|
||||
path: build/prefab/full/mac_arm64_gui
|
||||
make_mac_arm64_server_debug_build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install pip requirements
|
||||
run: tools/pcommand install_pip_reqs
|
||||
- name: Make the build
|
||||
run: make prefab-mac-arm64-server-debug-build
|
||||
- name: Upload the build
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: mac_arm64_server_(debug)
|
||||
path: build/prefab/full/mac_arm64_server
|
||||
make_windows_x86_gui_debug_build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install pip requirements
|
||||
run: tools/pcommand install_pip_reqs
|
||||
- name: Make the build
|
||||
run: make prefab-windows-x86-gui-debug-build
|
||||
- name: Upload the build
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: windows_x86_gui_(debug)
|
||||
path: build/prefab/full/windows_x86_gui
|
||||
make_windows_x86_server_debug_build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install pip requirements
|
||||
run: tools/pcommand install_pip_reqs
|
||||
- name: Make the build
|
||||
run: make prefab-windows-x86-server-debug-build
|
||||
- name: Upload the build
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: windows_x86_server_(debug)
|
||||
path: build/prefab/full/windows_x86_server
|
||||
1
.idea/ballisticakit.iml
generated
1
.idea/ballisticakit.iml
generated
@ -21,7 +21,6 @@
|
||||
<excludeFolder url="file://$MODULE_DIR$/ballisticakit-android" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/ballisticakit-cmake" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/ballisticakit-ios.xcodeproj" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/ballisticakit-mac.xcodeproj" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/ballisticakit-windows" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/ballisticakit-windows-oculus" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/ballisticakit-xcode" />
|
||||
|
||||
35
CHANGELOG.md
35
CHANGELOG.md
@ -1,4 +1,10 @@
|
||||
### 1.7.30 (build 21636, api 8, 2023-11-30)
|
||||
### 1.7.31 (build 21707, api 8, 2023-12-13)
|
||||
- Added `bascenev1.get_connection_to_host_info_2()` which is an improved
|
||||
type-safe version of `bascenev1.get_connection_to_host_info()`.
|
||||
- There is now a link to the official Discord server in the About section
|
||||
(thanks EraOSBeta!).
|
||||
|
||||
### 1.7.30 (build 21697, api 8, 2023-12-08)
|
||||
- Continued work on the big 1.7.28 update.
|
||||
- Got the Android version back up and running. There's been lots of cleanup and
|
||||
simplification on the Android layer, cleaning out years of cruft. This should
|
||||
@ -15,15 +21,34 @@
|
||||
- Bundled Android Python has been bumped to version 3.11.6.
|
||||
- Android app suspend behavior has been revamped. The app should stay running
|
||||
more often and be quicker to respond when dialogs or other activities
|
||||
temporarily pop up in front of it. Please holler if you run into strange side
|
||||
temporarily pop up in front of it. This also allows it to continue playing
|
||||
music over other activities such as Google Play Games
|
||||
Achievements/Leaderboards screens. Please holler if you run into strange side
|
||||
effects such as the app continuing to play audio when it should not be.
|
||||
- Modernized the Android fullscreen setup code when running in Android 11 or
|
||||
newer. The game should now use the whole screen area, including the area
|
||||
around notches or camera cutouts. Please holler if you are seeing any problems
|
||||
related to this.
|
||||
- (build 21626) Fixed a bug where click/tap locations were incorrect on some
|
||||
builds when tv-border was on (Thanks for the heads-up Loup(Dliwk's fan)!).
|
||||
- (build 21631) Fixes an issue where '^^^^^^^^^^^^^' lines in stack traces could
|
||||
get chopped into tiny bits each on their own line in the dev console.
|
||||
- Fixed a longstanding issue where multiple key presses simultaneously could
|
||||
cause multiple windows to pop up where only one is expected. Please holler if
|
||||
you still see this problem happening anywhere.
|
||||
- Hopefully finally fixed a longstanding issue where obscure cases such as
|
||||
multiple key presses simultaneously could cause multiple main menu windows to
|
||||
pop up. Please holler if you still see this problem happening anywhere. Also
|
||||
added a few related safety checks and warnings to help ensure UI code is free
|
||||
from such problems going forward. To make sure your custom UIs are behaving
|
||||
well in this system, do the following two things: 1) any time you call
|
||||
`set_main_menu_window()`, pass your existing main menu window root widget as
|
||||
`from_window`. 2) In any call that can lead to you switching the main menu
|
||||
window, check if your root widget is dead or transitioning out first and abort
|
||||
if it is. See any window in `ui_v1_lib` for examples.
|
||||
- (build 21691) Fixed a bug causing touches to not register in some cases on
|
||||
newer Android devices. (Huge thanks to JESWIN A J for helping me track that
|
||||
down!).
|
||||
- Temporarily removed the pause-the-game-when-backgrounded behavior for locally
|
||||
hosted games, mainly due to the code being hacky. Will try to restore this
|
||||
functionality in a cleaner way soon.
|
||||
|
||||
### 1.7.29 (build 21619, api 8, 2023-11-21)
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ height="50" alt="logo">
|
||||
|
||||
***-ica***: collection of things relating to a specific theme.
|
||||
|
||||
[](https://github.com/efroemling/ballistica/actions/workflows/ci.yml)
|
||||
[](https://github.com/efroemling/ballistica/actions/workflows/ci.yml) [](https://github.com/efroemling/ballistica/actions/workflows/cd.yml)
|
||||
|
||||
The Ballistica project is the foundation for
|
||||
[BombSquad](https://www.froemling.net/apps/bombsquad) and potentially other
|
||||
@ -52,7 +52,7 @@ want to keep that spirit alive as the Ballistica project moves forward. Whether
|
||||
this means making it easier to share mods, organize tournaments, join up with
|
||||
friends, teach each other some Python, or whatever else. Life is short; let's
|
||||
play some games. Or make them. Maybe both.
|
||||
|
||||
|
||||
### Frequently Asked Questions
|
||||
|
||||
* **Q: What's with this name? Is it BombSquad or Ballistica?**
|
||||
@ -86,4 +86,4 @@ Playstation / My Toaster??**
|
||||
for more details or the [Ballistica
|
||||
Downloads](https://ballistica.net/downloads) page for early test builds on
|
||||
some platforms.
|
||||
|
||||
|
||||
|
||||
1
ballisticakit-cmake/.idea/misc.xml
generated
1
ballisticakit-cmake/.idea/misc.xml
generated
@ -14,7 +14,6 @@
|
||||
<file path="$PROJECT_DIR$/../ballisticakit-android" />
|
||||
<file path="$PROJECT_DIR$/.idea" />
|
||||
<file path="$PROJECT_DIR$/../ballisticakit-ios.xcodeproj" />
|
||||
<file path="$PROJECT_DIR$/../ballisticakit-mac.xcodeproj" />
|
||||
<file path="$PROJECT_DIR$/../ballisticakit-windows" />
|
||||
<file path="$PROJECT_DIR$/../ballisticakit-xcode" />
|
||||
<file path="$PROJECT_DIR$/../build" />
|
||||
|
||||
@ -152,7 +152,6 @@ ctx.filter_dirs = {
|
||||
'ballisticakit-cmake',
|
||||
'ballisticakit-xcode/BallisticaKit.xcodeproj',
|
||||
'ballisticakit-ios.xcodeproj',
|
||||
'ballisticakit-mac.xcodeproj',
|
||||
'config',
|
||||
'src/assets/pdoc',
|
||||
}
|
||||
@ -195,6 +194,7 @@ ctx.filter_file_names = {
|
||||
'.projectile',
|
||||
'.editorconfig',
|
||||
'ci.yml',
|
||||
'cd.yml',
|
||||
'LICENSE',
|
||||
'cloudtool',
|
||||
'bacloud',
|
||||
|
||||
@ -20,7 +20,6 @@
|
||||
"ba_data/python/babase/__pycache__/_error.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/babase/__pycache__/_general.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/babase/__pycache__/_hooks.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/babase/__pycache__/_keyboard.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/babase/__pycache__/_language.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/babase/__pycache__/_login.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/babase/__pycache__/_math.cpython-311.opt-1.pyc",
|
||||
@ -50,7 +49,6 @@
|
||||
"ba_data/python/babase/_error.py",
|
||||
"ba_data/python/babase/_general.py",
|
||||
"ba_data/python/babase/_hooks.py",
|
||||
"ba_data/python/babase/_keyboard.py",
|
||||
"ba_data/python/babase/_language.py",
|
||||
"ba_data/python/babase/_login.py",
|
||||
"ba_data/python/babase/_math.py",
|
||||
@ -152,6 +150,7 @@
|
||||
"ba_data/python/bascenev1/__pycache__/_messages.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/bascenev1/__pycache__/_multiteamsession.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/bascenev1/__pycache__/_music.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/bascenev1/__pycache__/_net.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/bascenev1/__pycache__/_nodeactor.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/bascenev1/__pycache__/_player.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/bascenev1/__pycache__/_playlist.cpython-311.opt-1.pyc",
|
||||
@ -186,6 +185,7 @@
|
||||
"ba_data/python/bascenev1/_messages.py",
|
||||
"ba_data/python/bascenev1/_multiteamsession.py",
|
||||
"ba_data/python/bascenev1/_music.py",
|
||||
"ba_data/python/bascenev1/_net.py",
|
||||
"ba_data/python/bascenev1/_nodeactor.py",
|
||||
"ba_data/python/bascenev1/_player.py",
|
||||
"ba_data/python/bascenev1/_playlist.py",
|
||||
@ -352,10 +352,12 @@
|
||||
"ba_data/python/bauiv1/__init__.py",
|
||||
"ba_data/python/bauiv1/__pycache__/__init__.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/bauiv1/__pycache__/_hooks.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/bauiv1/__pycache__/_keyboard.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/bauiv1/__pycache__/_subsystem.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/bauiv1/__pycache__/_uitypes.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/bauiv1/__pycache__/onscreenkeyboard.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/bauiv1/_hooks.py",
|
||||
"ba_data/python/bauiv1/_keyboard.py",
|
||||
"ba_data/python/bauiv1/_subsystem.py",
|
||||
"ba_data/python/bauiv1/_uitypes.py",
|
||||
"ba_data/python/bauiv1/onscreenkeyboard.py",
|
||||
|
||||
@ -178,7 +178,6 @@ SCRIPT_TARGETS_PY_PUBLIC = \
|
||||
$(BUILD_DIR)/ba_data/python/babase/_error.py \
|
||||
$(BUILD_DIR)/ba_data/python/babase/_general.py \
|
||||
$(BUILD_DIR)/ba_data/python/babase/_hooks.py \
|
||||
$(BUILD_DIR)/ba_data/python/babase/_keyboard.py \
|
||||
$(BUILD_DIR)/ba_data/python/babase/_language.py \
|
||||
$(BUILD_DIR)/ba_data/python/babase/_login.py \
|
||||
$(BUILD_DIR)/ba_data/python/babase/_math.py \
|
||||
@ -237,6 +236,7 @@ SCRIPT_TARGETS_PY_PUBLIC = \
|
||||
$(BUILD_DIR)/ba_data/python/bascenev1/_messages.py \
|
||||
$(BUILD_DIR)/ba_data/python/bascenev1/_multiteamsession.py \
|
||||
$(BUILD_DIR)/ba_data/python/bascenev1/_music.py \
|
||||
$(BUILD_DIR)/ba_data/python/bascenev1/_net.py \
|
||||
$(BUILD_DIR)/ba_data/python/bascenev1/_nodeactor.py \
|
||||
$(BUILD_DIR)/ba_data/python/bascenev1/_player.py \
|
||||
$(BUILD_DIR)/ba_data/python/bascenev1/_playlist.py \
|
||||
@ -326,6 +326,7 @@ SCRIPT_TARGETS_PY_PUBLIC = \
|
||||
$(BUILD_DIR)/ba_data/python/batemplatefs/_subsystem.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1/__init__.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1/_hooks.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1/_keyboard.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1/_subsystem.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1/_uitypes.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1/onscreenkeyboard.py \
|
||||
@ -452,7 +453,6 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
|
||||
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_error.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_general.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_hooks.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_keyboard.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_language.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_login.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_math.cpython-311.opt-1.pyc \
|
||||
@ -511,6 +511,7 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
|
||||
$(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_messages.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_multiteamsession.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_music.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_net.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_nodeactor.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_player.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_playlist.cpython-311.opt-1.pyc \
|
||||
@ -600,6 +601,7 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
|
||||
$(BUILD_DIR)/ba_data/python/batemplatefs/__pycache__/_subsystem.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1/__pycache__/__init__.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1/__pycache__/_hooks.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1/__pycache__/_keyboard.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1/__pycache__/_subsystem.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1/__pycache__/_uitypes.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1/__pycache__/onscreenkeyboard.cpython-311.opt-1.pyc \
|
||||
|
||||
@ -154,7 +154,6 @@ from babase._general import (
|
||||
getclass,
|
||||
get_type_name,
|
||||
)
|
||||
from babase._keyboard import Keyboard
|
||||
from babase._language import Lstr, LanguageSubsystem
|
||||
from babase._login import LoginAdapter, LoginInfo
|
||||
|
||||
@ -261,7 +260,6 @@ __all__ = [
|
||||
'is_point_in_box',
|
||||
'is_running_on_fire_tv',
|
||||
'is_xcode_build',
|
||||
'Keyboard',
|
||||
'LanguageSubsystem',
|
||||
'lock_all_input',
|
||||
'LoginAdapter',
|
||||
|
||||
@ -186,9 +186,10 @@ class AccountV2Subsystem:
|
||||
cfgkey = 'ImplicitLoginStates'
|
||||
cfgdict = _babase.app.config.setdefault(cfgkey, {})
|
||||
|
||||
# Store which (if any) adapter is currently implicitly signed in.
|
||||
# Making the assumption there will only ever be one implicit
|
||||
# adapter at a time; may need to update this if that changes.
|
||||
# Store which (if any) adapter is currently implicitly signed
|
||||
# in. Making the assumption there will only ever be one implicit
|
||||
# adapter at a time; may need to revisit this logic if that
|
||||
# changes.
|
||||
prev_state = cfgdict.get(login_type.value)
|
||||
if state is None:
|
||||
self._implicit_signed_in_adapter = None
|
||||
@ -296,9 +297,8 @@ class AccountV2Subsystem:
|
||||
# Consider this an 'explicit' sign in because the
|
||||
# implicit-login state change presumably was triggered
|
||||
# by some user action (signing in, signing out, or
|
||||
# switching accounts via the back-end).
|
||||
# NOTE: should test case where we don't have
|
||||
# connectivity here.
|
||||
# switching accounts via the back-end). NOTE: should
|
||||
# test case where we don't have connectivity here.
|
||||
if plus.cloud.is_connected():
|
||||
if DEBUG_LOG:
|
||||
logging.debug(
|
||||
|
||||
@ -229,6 +229,15 @@ class App:
|
||||
self.lang = LanguageSubsystem()
|
||||
self.plugins = PluginSubsystem()
|
||||
|
||||
@property
|
||||
def active(self) -> bool:
|
||||
"""Whether the app is currently front and center.
|
||||
|
||||
This will be False when the app is hidden, other activities
|
||||
are covering it, etc. (depending on the platform).
|
||||
"""
|
||||
return _babase.app_is_active()
|
||||
|
||||
@property
|
||||
def aioloop(self) -> asyncio.AbstractEventLoop:
|
||||
"""The logic thread's asyncio event loop.
|
||||
|
||||
@ -31,6 +31,7 @@ class AppMode:
|
||||
AppExperience associated with the AppMode must be supported by
|
||||
the current app and runtime environment.
|
||||
"""
|
||||
# FIXME: check AppExperience.
|
||||
return cls._supports_intent(intent)
|
||||
|
||||
@classmethod
|
||||
|
||||
@ -325,7 +325,7 @@ def dump_app_state(
|
||||
)
|
||||
|
||||
|
||||
def log_dumped_app_state() -> None:
|
||||
def log_dumped_app_state(from_previous_run: bool = False) -> None:
|
||||
"""If an app-state dump exists, log it and clear it. No-op otherwise."""
|
||||
|
||||
try:
|
||||
@ -352,8 +352,13 @@ def log_dumped_app_state() -> None:
|
||||
|
||||
metadata = dataclass_from_json(DumpedAppStateMetadata, appstatedata)
|
||||
|
||||
header = (
|
||||
'Found app state dump from previous app run'
|
||||
if from_previous_run
|
||||
else 'App state dump'
|
||||
)
|
||||
out += (
|
||||
f'App state dump:\nReason: {metadata.reason}\n'
|
||||
f'{header}:\nReason: {metadata.reason}\n'
|
||||
f'Time: {metadata.app_time:.2f}'
|
||||
)
|
||||
tbpath = os.path.join(
|
||||
@ -383,7 +388,7 @@ class AppHealthMonitor(AppSubsystem):
|
||||
|
||||
def on_app_loading(self) -> None:
|
||||
# If any traceback dumps happened last run, log and clear them.
|
||||
log_dumped_app_state()
|
||||
log_dumped_app_state(from_previous_run=True)
|
||||
|
||||
def _app_monitor_thread_main(self) -> None:
|
||||
_babase.set_thread_name('ballistica app-monitor')
|
||||
|
||||
@ -145,7 +145,7 @@ class LoginAdapter:
|
||||
is actually being used by the app. It should therefore register
|
||||
unlocked achievements, leaderboard scores, allow viewing native
|
||||
UIs, etc. When not active it should ignore everything and behave
|
||||
as if logged out, even if it technically is still logged in.
|
||||
as if signed out, even if it technically is still signed in.
|
||||
"""
|
||||
assert _babase.in_logic_thread()
|
||||
del active # Unused.
|
||||
|
||||
@ -24,6 +24,8 @@ if TYPE_CHECKING:
|
||||
# instead of these or to make the meta system aware of arbitrary classes.
|
||||
EXPORT_CLASS_NAME_SHORTCUTS: dict[str, str] = {
|
||||
'plugin': 'babase.Plugin',
|
||||
# DEPRECATED as of 12/2023. Currently am warning if finding these
|
||||
# but should take this out eventually.
|
||||
'keyboard': 'babase.Keyboard',
|
||||
}
|
||||
|
||||
@ -414,30 +416,27 @@ class DirectoryScan:
|
||||
if export_class_name is not None:
|
||||
classname = modulename + '.' + export_class_name
|
||||
|
||||
# Since we'll soon have multiple versions of 'game'
|
||||
# classes we need to migrate people to using base
|
||||
# class names for them.
|
||||
if exporttypestr == 'game':
|
||||
# Migrating away from the 'keyboard' name shortcut
|
||||
# since it's specific to bauiv1; warn if we find it.
|
||||
if exporttypestr == 'keyboard':
|
||||
logging.warning(
|
||||
"metascan: %s:%d: '# ba_meta export"
|
||||
" game' tag should be replaced by '# ba_meta"
|
||||
" export bascenev1.GameActivity'.",
|
||||
" keyboard' tag should be replaced by '# ba_meta"
|
||||
" export bauiv1.Keyboard'.",
|
||||
subpath,
|
||||
lindex + 1,
|
||||
)
|
||||
self.results.announce_errors_occurred = True
|
||||
else:
|
||||
# If export type is one of our shortcuts, sub in the
|
||||
# actual class path. Otherwise assume its a classpath
|
||||
# itself.
|
||||
exporttype = EXPORT_CLASS_NAME_SHORTCUTS.get(
|
||||
exporttypestr
|
||||
)
|
||||
if exporttype is None:
|
||||
exporttype = exporttypestr
|
||||
self.results.exports.setdefault(exporttype, []).append(
|
||||
classname
|
||||
)
|
||||
|
||||
# If export type is one of our shortcuts, sub in the
|
||||
# actual class path. Otherwise assume its a classpath
|
||||
# itself.
|
||||
exporttype = EXPORT_CLASS_NAME_SHORTCUTS.get(exporttypestr)
|
||||
if exporttype is None:
|
||||
exporttype = exporttypestr
|
||||
self.results.exports.setdefault(exporttype, []).append(
|
||||
classname
|
||||
)
|
||||
|
||||
def _get_export_class_name(
|
||||
self, subpath: Path, lines: list[str], lindex: int
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import babase
|
||||
@ -31,6 +33,7 @@ class AdsSubsystem:
|
||||
self.last_in_game_ad_remove_message_show_time: float | None = None
|
||||
self.last_ad_completion_time: float | None = None
|
||||
self.last_ad_was_short = False
|
||||
self._fallback_task: asyncio.Task | None = None
|
||||
|
||||
def do_remove_in_game_ads_message(self) -> None:
|
||||
"""(internal)"""
|
||||
@ -94,7 +97,7 @@ class AdsSubsystem:
|
||||
show = True
|
||||
|
||||
# No ads without net-connections, etc.
|
||||
if not bauiv1.can_show_ad():
|
||||
if not plus.can_show_ad():
|
||||
show = False
|
||||
if classic.accounts.have_pro():
|
||||
show = False # Pro disables interstitials.
|
||||
@ -132,7 +135,7 @@ class AdsSubsystem:
|
||||
# ad-show-threshold and see if we should *actually* show
|
||||
# (we reach our threshold faster the longer we've been
|
||||
# playing).
|
||||
base = 'ads' if bauiv1.has_video_ads() else 'ads2'
|
||||
base = 'ads' if plus.has_video_ads() else 'ads2'
|
||||
min_lc = plus.get_v1_account_misc_read_val(base + '.minLC', 0.0)
|
||||
max_lc = plus.get_v1_account_misc_read_val(base + '.maxLC', 5.0)
|
||||
min_lc_scale = plus.get_v1_account_misc_read_val(
|
||||
@ -181,36 +184,53 @@ class AdsSubsystem:
|
||||
|
||||
# If we're *still* cleared to show, actually tell the system to show.
|
||||
if show:
|
||||
# As a safety-check, set up an object that will run
|
||||
# the completion callback if we've returned and sat for 10 seconds
|
||||
# (in case some random ad network doesn't properly deliver its
|
||||
# completion callback).
|
||||
# As a safety-check, we set up an object that will run the
|
||||
# completion callback if we've returned and sat for several
|
||||
# seconds (in case some random ad network doesn't properly
|
||||
# deliver its completion callback).
|
||||
class _Payload:
|
||||
def __init__(self, pcall: Callable[[], Any]):
|
||||
self._call = pcall
|
||||
self._ran = False
|
||||
|
||||
def run(self, fallback: bool = False) -> None:
|
||||
"""Run fallback call (and issue a warning about it)."""
|
||||
"""Run the payload."""
|
||||
assert app.classic is not None
|
||||
if not self._ran:
|
||||
if fallback:
|
||||
lanst = app.classic.ads.last_ad_network_set_time
|
||||
print(
|
||||
'ERROR: relying on fallback ad-callback! '
|
||||
'last network: '
|
||||
+ app.classic.ads.last_ad_network
|
||||
+ ' (set '
|
||||
+ str(int(time.time() - lanst))
|
||||
+ 's ago); purpose='
|
||||
+ app.classic.ads.last_ad_purpose
|
||||
logging.error(
|
||||
'Relying on fallback ad-callback! '
|
||||
'last network: %s (set %s seconds ago);'
|
||||
' purpose=%s.',
|
||||
app.classic.ads.last_ad_network,
|
||||
time.time() - lanst,
|
||||
app.classic.ads.last_ad_purpose,
|
||||
)
|
||||
babase.pushcall(self._call)
|
||||
self._ran = True
|
||||
|
||||
payload = _Payload(call)
|
||||
|
||||
# Set up our backup.
|
||||
with babase.ContextRef.empty():
|
||||
babase.apptimer(5.0, lambda: payload.run(fallback=True))
|
||||
# Note to self: Previously this was a simple 5 second
|
||||
# timer because the app got totally suspended while ads
|
||||
# were showing (which delayed the timer), but these days
|
||||
# the app may continue to run, so we need to be more
|
||||
# careful and only fire the fallback after we see that
|
||||
# the app has been front-and-center for several seconds.
|
||||
async def add_fallback_task() -> None:
|
||||
activesecs = 5
|
||||
while activesecs > 0:
|
||||
if babase.app.active:
|
||||
activesecs -= 1
|
||||
await asyncio.sleep(1.0)
|
||||
payload.run(fallback=True)
|
||||
|
||||
_fallback_task = babase.app.aioloop.create_task(
|
||||
add_fallback_task()
|
||||
)
|
||||
self.show_ad('between_game', on_completion_call=payload.run)
|
||||
else:
|
||||
babase.pushcall(call) # Just run the callback without the ad.
|
||||
|
||||
@ -41,5 +41,6 @@ class AppDelegate:
|
||||
sessiontype,
|
||||
settings,
|
||||
completion_call=completion_call,
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=False, # Disable check since we don't know.
|
||||
)
|
||||
|
||||
@ -800,5 +800,6 @@ class ClassicSubsystem(babase.AppSubsystem):
|
||||
bauiv1.getsound('swish').play()
|
||||
|
||||
babase.app.ui_v1.set_main_menu_window(
|
||||
MainMenuWindow().get_root_widget()
|
||||
MainMenuWindow().get_root_widget(),
|
||||
from_window=False, # Disable check here.
|
||||
)
|
||||
|
||||
@ -52,8 +52,8 @@ if TYPE_CHECKING:
|
||||
|
||||
# Build number and version of the ballistica binary we expect to be
|
||||
# using.
|
||||
TARGET_BALLISTICA_BUILD = 21636
|
||||
TARGET_BALLISTICA_VERSION = '1.7.30'
|
||||
TARGET_BALLISTICA_BUILD = 21707
|
||||
TARGET_BALLISTICA_VERSION = '1.7.31'
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
@ -249,3 +249,18 @@ class PlusSubsystem(AppSubsystem):
|
||||
) -> None:
|
||||
"""(internal)"""
|
||||
return _baplus.tournament_query(callback, args)
|
||||
|
||||
@staticmethod
|
||||
def have_incentivized_ad() -> bool:
|
||||
"""Is an incentivized ad available?"""
|
||||
return _baplus.have_incentivized_ad()
|
||||
|
||||
@staticmethod
|
||||
def has_video_ads() -> bool:
|
||||
"""Are video ads available?"""
|
||||
return _baplus.has_video_ads()
|
||||
|
||||
@staticmethod
|
||||
def can_show_ad() -> bool:
|
||||
"""Can we show an ad?"""
|
||||
return _baplus.can_show_ad()
|
||||
|
||||
@ -78,6 +78,7 @@ from _bascenev1 import (
|
||||
end_host_scanning,
|
||||
get_chat_messages,
|
||||
get_connection_to_host_info,
|
||||
get_connection_to_host_info_2,
|
||||
get_foreground_host_activity,
|
||||
get_foreground_host_session,
|
||||
get_game_port,
|
||||
@ -202,6 +203,7 @@ from bascenev1._multiteamsession import (
|
||||
DEFAULT_TEAM_NAMES,
|
||||
)
|
||||
from bascenev1._music import MusicType, setmusic
|
||||
from bascenev1._net import HostInfo
|
||||
from bascenev1._nodeactor import NodeActor
|
||||
from bascenev1._powerup import get_default_powerup_distribution
|
||||
from bascenev1._profile import (
|
||||
@ -303,6 +305,7 @@ __all__ = [
|
||||
'GameTip',
|
||||
'get_chat_messages',
|
||||
'get_connection_to_host_info',
|
||||
'get_connection_to_host_info_2',
|
||||
'get_default_free_for_all_playlist',
|
||||
'get_default_teams_playlist',
|
||||
'get_default_powerup_distribution',
|
||||
@ -338,6 +341,7 @@ __all__ = [
|
||||
'have_connected_clients',
|
||||
'have_touchscreen_input',
|
||||
'HitMessage',
|
||||
'HostInfo',
|
||||
'host_scan_cycle',
|
||||
'ImpactDamageMessage',
|
||||
'increment_analytics_count',
|
||||
|
||||
24
src/assets/ba_data/python/bascenev1/_net.py
Normal file
24
src/assets/ba_data/python/bascenev1/_net.py
Normal file
@ -0,0 +1,24 @@
|
||||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Functionality related to net play."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from dataclasses import dataclass
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
|
||||
@dataclass
|
||||
class HostInfo:
|
||||
"""Info about a host."""
|
||||
|
||||
name: str
|
||||
build_number: int
|
||||
|
||||
# Note this can be None for non-ip hosts such as bluetooth.
|
||||
address: str | None
|
||||
|
||||
# Note this can be None for non-ip hosts such as bluetooth.
|
||||
port: int | None
|
||||
@ -190,7 +190,7 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
||||
super().__del__()
|
||||
|
||||
# If our UI is still up, kill it.
|
||||
if self._root_ui:
|
||||
if self._root_ui and not self._root_ui.transitioning_out:
|
||||
with bui.ContextRef.empty():
|
||||
bui.containerwidget(edit=self._root_ui, transition='out_left')
|
||||
|
||||
|
||||
@ -317,7 +317,8 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]):
|
||||
from bauiv1lib.kiosk import KioskWindow
|
||||
|
||||
bs.app.ui_v1.set_main_menu_window(
|
||||
KioskWindow().get_root_widget()
|
||||
KioskWindow().get_root_widget(),
|
||||
from_window=False, # Disable check here.
|
||||
)
|
||||
# ..or in normal cases go back to the main menu
|
||||
else:
|
||||
@ -326,14 +327,16 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]):
|
||||
from bauiv1lib.gather import GatherWindow
|
||||
|
||||
bs.app.ui_v1.set_main_menu_window(
|
||||
GatherWindow(transition=None).get_root_widget()
|
||||
GatherWindow(transition=None).get_root_widget(),
|
||||
from_window=False, # Disable check here.
|
||||
)
|
||||
elif main_menu_location == 'Watch':
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.watch import WatchWindow
|
||||
|
||||
bs.app.ui_v1.set_main_menu_window(
|
||||
WatchWindow(transition=None).get_root_widget()
|
||||
WatchWindow(transition=None).get_root_widget(),
|
||||
from_window=False, # Disable check here.
|
||||
)
|
||||
elif main_menu_location == 'Team Game Select':
|
||||
# pylint: disable=cyclic-import
|
||||
@ -344,7 +347,8 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]):
|
||||
bs.app.ui_v1.set_main_menu_window(
|
||||
PlaylistBrowserWindow(
|
||||
sessiontype=bs.DualTeamSession, transition=None
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=False, # Disable check here.
|
||||
)
|
||||
elif main_menu_location == 'Free-for-All Game Select':
|
||||
# pylint: disable=cyclic-import
|
||||
@ -356,28 +360,34 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]):
|
||||
PlaylistBrowserWindow(
|
||||
sessiontype=bs.FreeForAllSession,
|
||||
transition=None,
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=False, # Disable check here.
|
||||
)
|
||||
elif main_menu_location == 'Coop Select':
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.coop.browser import CoopBrowserWindow
|
||||
|
||||
bs.app.ui_v1.set_main_menu_window(
|
||||
CoopBrowserWindow(transition=None).get_root_widget()
|
||||
CoopBrowserWindow(
|
||||
transition=None
|
||||
).get_root_widget(),
|
||||
from_window=False, # Disable check here.
|
||||
)
|
||||
elif main_menu_location == 'Benchmarks & Stress Tests':
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.debug import DebugWindow
|
||||
|
||||
bs.app.ui_v1.set_main_menu_window(
|
||||
DebugWindow(transition=None).get_root_widget()
|
||||
DebugWindow(transition=None).get_root_widget(),
|
||||
from_window=False, # Disable check here.
|
||||
)
|
||||
else:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.mainmenu import MainMenuWindow
|
||||
|
||||
bs.app.ui_v1.set_main_menu_window(
|
||||
MainMenuWindow(transition=None).get_root_widget()
|
||||
MainMenuWindow(transition=None).get_root_widget(),
|
||||
from_window=None,
|
||||
)
|
||||
|
||||
# attempt to show any pending offers immediately.
|
||||
|
||||
@ -62,7 +62,6 @@ from babase import (
|
||||
is_browser_likely_available,
|
||||
is_running_on_fire_tv,
|
||||
is_xcode_build,
|
||||
Keyboard,
|
||||
lock_all_input,
|
||||
LoginAdapter,
|
||||
LoginInfo,
|
||||
@ -94,7 +93,6 @@ from babase import (
|
||||
|
||||
from _bauiv1 import (
|
||||
buttonwidget,
|
||||
can_show_ad,
|
||||
checkboxwidget,
|
||||
columnwidget,
|
||||
containerwidget,
|
||||
@ -103,8 +101,6 @@ from _bauiv1 import (
|
||||
getmesh,
|
||||
getsound,
|
||||
gettexture,
|
||||
has_video_ads,
|
||||
have_incentivized_ad,
|
||||
hscrollwidget,
|
||||
imagewidget,
|
||||
is_party_icon_visible,
|
||||
@ -125,6 +121,7 @@ from _bauiv1 import (
|
||||
Widget,
|
||||
widget,
|
||||
)
|
||||
from bauiv1._keyboard import Keyboard
|
||||
from bauiv1._uitypes import Window, uicleanupcheck
|
||||
from bauiv1._subsystem import UIV1Subsystem
|
||||
|
||||
@ -144,7 +141,6 @@ __all__ = [
|
||||
'AppTimer',
|
||||
'buttonwidget',
|
||||
'Call',
|
||||
'can_show_ad',
|
||||
'fullscreen_control_available',
|
||||
'fullscreen_control_get',
|
||||
'fullscreen_control_key_shortcut',
|
||||
@ -178,8 +174,6 @@ __all__ = [
|
||||
'getmesh',
|
||||
'getsound',
|
||||
'gettexture',
|
||||
'has_video_ads',
|
||||
'have_incentivized_ad',
|
||||
'have_permission',
|
||||
'hscrollwidget',
|
||||
'imagewidget',
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import inspect
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import _bauiv1
|
||||
@ -87,3 +88,19 @@ def show_url_window(address: str) -> None:
|
||||
return
|
||||
|
||||
app.classic.show_url_window(address)
|
||||
|
||||
|
||||
def double_transition_out_warning() -> None:
|
||||
"""Called if a widget is set to transition out twice."""
|
||||
caller_frame = inspect.stack()[1]
|
||||
caller_filename = caller_frame.filename
|
||||
caller_line_number = caller_frame.lineno
|
||||
logging.warning(
|
||||
'ContainerWidget was set to transition out twice;'
|
||||
' this often implies buggy code (%s line %s).\n'
|
||||
' Generally you should check the value of'
|
||||
' _root_widget.transitioning_out and only kick off transitions'
|
||||
' when that is False.',
|
||||
caller_filename,
|
||||
caller_line_number,
|
||||
)
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import inspect
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import babase
|
||||
@ -116,21 +117,69 @@ class UIV1Subsystem(babase.AppSubsystem):
|
||||
# FIXME: Can probably kill this if we do immediate UI death checks.
|
||||
self.upkeeptimer = babase.AppTimer(2.6543, ui_upkeep, repeat=True)
|
||||
|
||||
def set_main_menu_window(self, window: bauiv1.Widget) -> None:
|
||||
"""Set the current 'main' window, replacing any existing."""
|
||||
def set_main_menu_window(
|
||||
self,
|
||||
window: bauiv1.Widget,
|
||||
from_window: bauiv1.Widget | None | bool = True,
|
||||
) -> None:
|
||||
"""Set the current 'main' window, replacing any existing.
|
||||
|
||||
If 'from_window' is passed as a bauiv1.Widget or None, a warning
|
||||
will be issued if it that value does not match the current main
|
||||
window. This can help clean up flawed code that can lead to bad
|
||||
UI states. A value of False will disable the check.
|
||||
"""
|
||||
|
||||
existing = self._main_menu_window
|
||||
from inspect import currentframe, getframeinfo
|
||||
|
||||
try:
|
||||
if isinstance(from_window, bool):
|
||||
# For default val True we warn that the arg wasn't
|
||||
# passed. False can be explicitly passed to disable this
|
||||
# check.
|
||||
if from_window is True:
|
||||
caller_frame = inspect.stack()[1]
|
||||
caller_filename = caller_frame.filename
|
||||
caller_line_number = caller_frame.lineno
|
||||
logging.warning(
|
||||
'set_main_menu_window() should be passed a'
|
||||
" 'from_window' value to help ensure proper UI behavior"
|
||||
' (%s line %i).',
|
||||
caller_filename,
|
||||
caller_line_number,
|
||||
)
|
||||
else:
|
||||
# For everything else, warn if what they passed wasn't
|
||||
# the previous main menu widget.
|
||||
if from_window is not existing:
|
||||
caller_frame = inspect.stack()[1]
|
||||
caller_filename = caller_frame.filename
|
||||
caller_line_number = caller_frame.lineno
|
||||
logging.warning(
|
||||
"set_main_menu_window() was passed 'from_window' %s"
|
||||
' but existing main-menu-window is %s. (%s line %i).',
|
||||
from_window,
|
||||
existing,
|
||||
caller_filename,
|
||||
caller_line_number,
|
||||
)
|
||||
except Exception:
|
||||
# Prevent any bugs in these checks from causing problems.
|
||||
logging.exception('Error checking from_window')
|
||||
|
||||
# Once the above code leads to us fixing all leftover window bugs
|
||||
# at the source, we can kill the code below.
|
||||
|
||||
# Let's grab the location where we were called from to report
|
||||
# if we have to force-kill the existing window (which normally
|
||||
# should not happen).
|
||||
frameline = None
|
||||
try:
|
||||
frame = currentframe()
|
||||
frame = inspect.currentframe()
|
||||
if frame is not None:
|
||||
frame = frame.f_back
|
||||
if frame is not None:
|
||||
frameinfo = getframeinfo(frame)
|
||||
frameinfo = inspect.getframeinfo(frame)
|
||||
frameline = f'{frameinfo.filename} {frameinfo.lineno}'
|
||||
except Exception:
|
||||
logging.exception('Error calcing line for set_main_menu_window')
|
||||
@ -160,13 +209,18 @@ class UIV1Subsystem(babase.AppSubsystem):
|
||||
|
||||
def clear_main_menu_window(self, transition: str | None = None) -> None:
|
||||
"""Clear any existing 'main' window with the provided transition."""
|
||||
assert transition is None or not transition.endswith('_in')
|
||||
if self._main_menu_window:
|
||||
if transition is not None:
|
||||
if (
|
||||
transition is not None
|
||||
and not self._main_menu_window.transitioning_out
|
||||
):
|
||||
_bauiv1.containerwidget(
|
||||
edit=self._main_menu_window, transition=transition
|
||||
)
|
||||
else:
|
||||
self._main_menu_window.delete()
|
||||
self._main_menu_window = None
|
||||
|
||||
def add_main_menu_close_callback(self, call: Callable[[], Any]) -> None:
|
||||
"""(internal)"""
|
||||
|
||||
@ -12,6 +12,7 @@ from typing import TYPE_CHECKING
|
||||
import babase
|
||||
|
||||
import _bauiv1
|
||||
from bauiv1._keyboard import Keyboard
|
||||
from bauiv1._uitypes import Window
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -252,9 +253,7 @@ class OnScreenKeyboardWindow(Window):
|
||||
# Show change instructions only if we have more than one
|
||||
# keyboard option.
|
||||
keyboards = (
|
||||
babase.app.meta.scanresults.exports_of_class(
|
||||
babase.Keyboard
|
||||
)
|
||||
babase.app.meta.scanresults.exports_of_class(Keyboard)
|
||||
if babase.app.meta.scanresults is not None
|
||||
else []
|
||||
)
|
||||
@ -286,10 +285,10 @@ class OnScreenKeyboardWindow(Window):
|
||||
|
||||
def _get_keyboard(self) -> bui.Keyboard:
|
||||
assert babase.app.meta.scanresults is not None
|
||||
classname = babase.app.meta.scanresults.exports_of_class(
|
||||
babase.Keyboard
|
||||
)[self._keyboard_index]
|
||||
kbclass = babase.getclass(classname, babase.Keyboard)
|
||||
classname = babase.app.meta.scanresults.exports_of_class(Keyboard)[
|
||||
self._keyboard_index
|
||||
]
|
||||
kbclass = babase.getclass(classname, Keyboard)
|
||||
return kbclass()
|
||||
|
||||
def _refresh(self) -> None:
|
||||
@ -384,9 +383,7 @@ class OnScreenKeyboardWindow(Window):
|
||||
|
||||
def _next_keyboard(self) -> None:
|
||||
assert babase.app.meta.scanresults is not None
|
||||
kbexports = babase.app.meta.scanresults.exports_of_class(
|
||||
babase.Keyboard
|
||||
)
|
||||
kbexports = babase.app.meta.scanresults.exports_of_class(Keyboard)
|
||||
self._keyboard_index = (self._keyboard_index + 1) % len(kbexports)
|
||||
|
||||
self._load_keyboard()
|
||||
|
||||
@ -1507,9 +1507,18 @@ class AccountSettingsWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.profile.browser import ProfileBrowserWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
ProfileBrowserWindow(origin_widget=self._player_profiles_button)
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
ProfileBrowserWindow(
|
||||
origin_widget=self._player_profiles_button
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _cancel_sign_in_press(self) -> None:
|
||||
# If we're waiting on an adapter to give us credentials, abort.
|
||||
@ -1670,6 +1679,10 @@ class AccountSettingsWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.mainmenu import MainMenuWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
@ -1678,7 +1691,8 @@ class AccountSettingsWindow(bui.Window):
|
||||
if not self._modal:
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
MainMenuWindow(transition='in_left').get_root_widget()
|
||||
MainMenuWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _save_state(self) -> None:
|
||||
|
||||
@ -415,7 +415,7 @@ class CoopBrowserWindow(bui.Window):
|
||||
)
|
||||
|
||||
# Decrement time on our tournament buttons.
|
||||
ads_enabled = bui.have_incentivized_ad()
|
||||
ads_enabled = plus.have_incentivized_ad()
|
||||
for tbtn in self._tournament_buttons:
|
||||
tbtn.time_remaining = max(0, tbtn.time_remaining - 1)
|
||||
if tbtn.time_remaining_value_text is not None:
|
||||
@ -430,7 +430,7 @@ class CoopBrowserWindow(bui.Window):
|
||||
)
|
||||
|
||||
# Also adjust the ad icon visibility.
|
||||
if tbtn.allow_ads and bui.has_video_ads():
|
||||
if tbtn.allow_ads and plus.has_video_ads():
|
||||
bui.imagewidget(
|
||||
edit=tbtn.entry_fee_ad_image,
|
||||
opacity=1.0 if ads_enabled else 0.25,
|
||||
@ -1019,6 +1019,10 @@ class CoopBrowserWindow(bui.Window):
|
||||
from bauiv1lib.account import show_sign_in_prompt
|
||||
from bauiv1lib.league.rankwindow import LeagueRankWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
@ -1032,7 +1036,8 @@ class CoopBrowserWindow(bui.Window):
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
LeagueRankWindow(
|
||||
origin_widget=self._league_rank_button.get_button()
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _switch_to_score(
|
||||
@ -1043,6 +1048,10 @@ class CoopBrowserWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.account import show_sign_in_prompt
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
@ -1058,7 +1067,8 @@ class CoopBrowserWindow(bui.Window):
|
||||
origin_widget=self._store_button.get_button(),
|
||||
show_tab=show_tab,
|
||||
back_location='CoopBrowserWindow',
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def is_tourney_data_up_to_date(self) -> bool:
|
||||
@ -1218,6 +1228,10 @@ class CoopBrowserWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.play import PlayWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
# If something is selected, store it.
|
||||
self._save_state()
|
||||
bui.containerwidget(
|
||||
@ -1225,7 +1239,8 @@ class CoopBrowserWindow(bui.Window):
|
||||
)
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PlayWindow(transition='in_left').get_root_widget()
|
||||
PlayWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _save_state(self) -> None:
|
||||
|
||||
@ -638,8 +638,8 @@ class TournamentButton:
|
||||
|
||||
# Now, if this fee allows ads and we support video ads, show
|
||||
# the 'or ad' version.
|
||||
if allow_ads and bui.has_video_ads():
|
||||
ads_enabled = bui.have_incentivized_ad()
|
||||
if allow_ads and plus.has_video_ads():
|
||||
ads_enabled = plus.have_incentivized_ad()
|
||||
bui.imagewidget(
|
||||
edit=self.entry_fee_ad_image,
|
||||
opacity=1.0 if ads_enabled else 0.25,
|
||||
|
||||
@ -359,10 +359,15 @@ class CreditsListWindow(bui.Window):
|
||||
def _back(self) -> None:
|
||||
from bauiv1lib.mainmenu import MainMenuWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
)
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
MainMenuWindow(transition='in_left').get_root_widget()
|
||||
MainMenuWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
@ -379,8 +379,13 @@ class DebugWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.advanced import AdvancedSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_right')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
AdvancedSettingsWindow(transition='in_left').get_root_widget()
|
||||
AdvancedSettingsWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
@ -73,6 +73,7 @@ class DiscordWindow(bui.Window):
|
||||
edit=self._root_widget, cancel_button=self._back_button
|
||||
)
|
||||
|
||||
# Do we need to translate 'Discord'? Or is that always the name?
|
||||
self._title_text = bui.textwidget(
|
||||
parent=self._root_widget,
|
||||
position=(0, self._height - 52),
|
||||
@ -91,6 +92,9 @@ class DiscordWindow(bui.Window):
|
||||
texture=bui.gettexture('discordServer'),
|
||||
)
|
||||
|
||||
# Hmm should we translate this? The discord server is mostly
|
||||
# English so being able to read this might be a good screening
|
||||
# process?..
|
||||
bui.textwidget(
|
||||
parent=self._root_widget,
|
||||
position=(self._width / 2 - 60, self._height - 100),
|
||||
@ -110,7 +114,7 @@ class DiscordWindow(bui.Window):
|
||||
position=(self._width / 2 - 30, 20),
|
||||
size=(self._width / 2 - 60, 60),
|
||||
autoselect=True,
|
||||
label='Join The Discord',
|
||||
label=bui.Lstr(resource='discordJoinText'),
|
||||
text_scale=1.0,
|
||||
on_activate_call=bui.Call(
|
||||
bui.open_url, 'https://ballistica.net/discord'
|
||||
|
||||
@ -270,12 +270,17 @@ class GatherWindow(bui.Window):
|
||||
"""Called by the private-hosting tab to select a playlist."""
|
||||
from bauiv1lib.play import PlayWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.selecting_private_party_playlist = True
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PlayWindow(origin_widget=origin_widget).get_root_widget()
|
||||
PlayWindow(origin_widget=origin_widget).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _set_tab(self, tab_id: TabID) -> None:
|
||||
@ -383,11 +388,16 @@ class GatherWindow(bui.Window):
|
||||
def _back(self) -> None:
|
||||
from bauiv1lib.mainmenu import MainMenuWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
)
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
MainMenuWindow(transition='in_left').get_root_widget()
|
||||
MainMenuWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
@ -16,10 +16,6 @@ if TYPE_CHECKING:
|
||||
class AboutGatherTab(GatherTab):
|
||||
"""The about tab in the gather UI"""
|
||||
|
||||
def __init__(self, window: GatherWindow) -> None:
|
||||
super().__init__(window)
|
||||
self._container: bui.Widget | None = None
|
||||
|
||||
def on_activate(
|
||||
self,
|
||||
parent_widget: bui.Widget,
|
||||
@ -34,6 +30,40 @@ class AboutGatherTab(GatherTab):
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
try_tickets = plus.get_v1_account_misc_read_val(
|
||||
'friendTryTickets', None
|
||||
)
|
||||
|
||||
show_message = True
|
||||
# Squish message as needed to get things to fit nicely at
|
||||
# various scales.
|
||||
uiscale = bui.app.ui_v1.uiscale
|
||||
message_height = (
|
||||
210
|
||||
if uiscale is bui.UIScale.SMALL
|
||||
else 305
|
||||
if uiscale is bui.UIScale.MEDIUM
|
||||
else 370
|
||||
)
|
||||
# Let's not talk about sharing in vr-mode; its tricky to fit more
|
||||
# than one head in a VR-headset.
|
||||
show_message_extra = not bui.app.env.vr
|
||||
message_extra_height = 60
|
||||
show_invite = try_tickets is not None
|
||||
invite_height = 80
|
||||
show_discord = True
|
||||
discord_height = 80
|
||||
|
||||
c_height = 0
|
||||
if show_message:
|
||||
c_height += message_height
|
||||
if show_message_extra:
|
||||
c_height += message_extra_height
|
||||
if show_invite:
|
||||
c_height += invite_height
|
||||
if show_discord:
|
||||
c_height += discord_height
|
||||
|
||||
party_button_label = bui.charstr(bui.SpecialChar.TOP_BUTTON)
|
||||
message = bui.Lstr(
|
||||
resource='gatherWindow.aboutDescriptionText',
|
||||
@ -43,9 +73,7 @@ class AboutGatherTab(GatherTab):
|
||||
],
|
||||
)
|
||||
|
||||
# Let's not talk about sharing in vr-mode; its tricky to fit more
|
||||
# than one head in a VR-headset ;-)
|
||||
if not bui.app.env.vr:
|
||||
if show_message_extra:
|
||||
message = bui.Lstr(
|
||||
value='${A}\n\n${B}',
|
||||
subs=[
|
||||
@ -59,46 +87,52 @@ class AboutGatherTab(GatherTab):
|
||||
),
|
||||
],
|
||||
)
|
||||
string_height = 400
|
||||
include_invite = True
|
||||
include_discord = False # Need to fix spacing on small first.
|
||||
msc_scale = 1.1
|
||||
c_height_2 = min(region_height, string_height * msc_scale + 100)
|
||||
try_tickets = plus.get_v1_account_misc_read_val(
|
||||
'friendTryTickets', None
|
||||
)
|
||||
|
||||
if try_tickets is None:
|
||||
include_invite = False
|
||||
self._container = bui.containerwidget(
|
||||
scroll_widget = bui.scrollwidget(
|
||||
parent=parent_widget,
|
||||
position=(region_left, region_bottom),
|
||||
size=(region_width, region_height),
|
||||
highlight=False,
|
||||
border_opacity=0,
|
||||
)
|
||||
msc_scale = 1.1
|
||||
|
||||
container = bui.containerwidget(
|
||||
parent=scroll_widget,
|
||||
position=(
|
||||
region_left,
|
||||
region_bottom + (region_height - c_height_2) * 0.5,
|
||||
region_bottom + (region_height - c_height) * 0.5,
|
||||
),
|
||||
size=(region_width, c_height_2),
|
||||
size=(region_width, c_height),
|
||||
background=False,
|
||||
selectable=include_invite or include_discord,
|
||||
selectable=show_invite or show_discord,
|
||||
)
|
||||
bui.widget(edit=self._container, up_widget=tab_button)
|
||||
# Allows escaping if we select the container somehow (though
|
||||
# shouldn't be possible when buttons are present).
|
||||
bui.widget(edit=container, up_widget=tab_button)
|
||||
|
||||
bui.textwidget(
|
||||
parent=self._container,
|
||||
position=(region_width * 0.5, c_height_2 * 0.58),
|
||||
color=(0.6, 1.0, 0.6),
|
||||
scale=msc_scale,
|
||||
size=(0, 0),
|
||||
maxwidth=region_width * 0.9,
|
||||
max_height=c_height_2 * 0.7,
|
||||
h_align='center',
|
||||
v_align='center',
|
||||
text=message,
|
||||
)
|
||||
|
||||
if include_invite:
|
||||
y = c_height - 30
|
||||
if show_message:
|
||||
bui.textwidget(
|
||||
parent=self._container,
|
||||
position=(region_width * 0.57, 35),
|
||||
parent=container,
|
||||
position=(region_width * 0.5, y),
|
||||
color=(0.6, 1.0, 0.6),
|
||||
scale=msc_scale,
|
||||
size=(0, 0),
|
||||
maxwidth=region_width * 0.9,
|
||||
max_height=message_height,
|
||||
h_align='center',
|
||||
v_align='top',
|
||||
text=message,
|
||||
)
|
||||
y -= message_height
|
||||
if show_message_extra:
|
||||
y -= message_extra_height
|
||||
|
||||
if show_invite:
|
||||
bui.textwidget(
|
||||
parent=container,
|
||||
position=(region_width * 0.57, y),
|
||||
color=(0, 1, 0),
|
||||
scale=0.6,
|
||||
size=(0, 0),
|
||||
@ -112,8 +146,8 @@ class AboutGatherTab(GatherTab):
|
||||
),
|
||||
)
|
||||
invite_button = bui.buttonwidget(
|
||||
parent=self._container,
|
||||
position=(region_width * 0.59, 10),
|
||||
parent=container,
|
||||
position=(region_width * 0.59, y - 25),
|
||||
size=(230, 50),
|
||||
color=(0.54, 0.42, 0.56),
|
||||
textcolor=(0, 1, 0),
|
||||
@ -125,13 +159,14 @@ class AboutGatherTab(GatherTab):
|
||||
on_activate_call=bui.WeakCall(self._invite_to_try_press),
|
||||
up_widget=tab_button,
|
||||
)
|
||||
y -= invite_height
|
||||
else:
|
||||
invite_button = None
|
||||
|
||||
if include_discord:
|
||||
if show_discord:
|
||||
bui.textwidget(
|
||||
parent=self._container,
|
||||
position=(region_width * 0.57, 15 if include_invite else 75),
|
||||
parent=container,
|
||||
position=(region_width * 0.57, y),
|
||||
color=(0.6, 0.6, 1),
|
||||
scale=0.6,
|
||||
size=(0, 0),
|
||||
@ -139,26 +174,29 @@ class AboutGatherTab(GatherTab):
|
||||
h_align='right',
|
||||
v_align='center',
|
||||
flatness=1.0,
|
||||
text=(
|
||||
'Want to look for new people to play with?\n'
|
||||
'Join our Discord and find new friends!'
|
||||
),
|
||||
text=bui.Lstr(resource='discordFriendsText'),
|
||||
)
|
||||
bui.buttonwidget(
|
||||
parent=self._container,
|
||||
position=(region_width * 0.59, -10 if include_invite else 50),
|
||||
discord_button = bui.buttonwidget(
|
||||
parent=container,
|
||||
position=(region_width * 0.59, y - 25),
|
||||
size=(230, 50),
|
||||
color=(0.54, 0.42, 0.56),
|
||||
textcolor=(0.6, 0.6, 1),
|
||||
label='Join The Discord',
|
||||
label=bui.Lstr(resource='discordJoinText'),
|
||||
autoselect=True,
|
||||
on_activate_call=bui.WeakCall(self._join_the_discord_press),
|
||||
up_widget=(
|
||||
invite_button if invite_button is not None else tab_button
|
||||
),
|
||||
)
|
||||
y -= discord_height
|
||||
else:
|
||||
discord_button = None
|
||||
|
||||
return self._container
|
||||
if discord_button is not None:
|
||||
pass
|
||||
|
||||
return scroll_widget
|
||||
|
||||
def _invite_to_try_press(self) -> None:
|
||||
from bauiv1lib.account import show_sign_in_prompt
|
||||
|
||||
@ -334,7 +334,7 @@ class GetCurrencyWindow(bui.Window):
|
||||
tex_scale=1.2,
|
||||
) # 19.99-ish
|
||||
|
||||
self._enable_ad_button = bui.has_video_ads()
|
||||
self._enable_ad_button = plus.has_video_ads()
|
||||
h = self._width * 0.5 + 110.0
|
||||
v = self._height - b_size[1] - 115.0
|
||||
|
||||
@ -561,7 +561,7 @@ class GetCurrencyWindow(bui.Window):
|
||||
next_reward_ad_time
|
||||
)
|
||||
now = datetime.datetime.utcnow()
|
||||
if bui.have_incentivized_ad() and (
|
||||
if plus.have_incentivized_ad() and (
|
||||
next_reward_ad_time is None or next_reward_ad_time <= now
|
||||
):
|
||||
self._ad_button_greyed = False
|
||||
@ -732,8 +732,13 @@ class GetCurrencyWindow(bui.Window):
|
||||
def _back(self) -> None:
|
||||
from bauiv1lib.store import browser
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
if self._transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
)
|
||||
@ -745,7 +750,9 @@ class GetCurrencyWindow(bui.Window):
|
||||
).get_root_widget()
|
||||
if not self._from_modal_store:
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(window)
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
window, from_window=self._root_widget
|
||||
)
|
||||
self._transitioning_out = True
|
||||
|
||||
|
||||
|
||||
@ -645,11 +645,16 @@ class HelpWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.mainmenu import MainMenuWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
)
|
||||
if self._main_menu:
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
MainMenuWindow(transition='in_left').get_root_widget()
|
||||
MainMenuWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
@ -9,7 +9,7 @@ from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import babase
|
||||
import bauiv1 as bui
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Iterable
|
||||
@ -33,15 +33,15 @@ def split(chars: Iterable[str], maxlen: int) -> list[list[str]]:
|
||||
|
||||
|
||||
def generate_emojis(maxlen: int) -> list[list[str]]:
|
||||
"""Generates a lot of UTF8 emojis prepared for babase.Keyboard pages"""
|
||||
"""Generates a lot of UTF8 emojis prepared for bui.Keyboard pages"""
|
||||
all_emojis = split([chr(i) for i in range(0x1F601, 0x1F650)], maxlen)
|
||||
all_emojis += split([chr(i) for i in range(0x2702, 0x27B1)], maxlen)
|
||||
all_emojis += split([chr(i) for i in range(0x1F680, 0x1F6C1)], maxlen)
|
||||
return all_emojis
|
||||
|
||||
|
||||
# ba_meta export keyboard
|
||||
class EnglishKeyboard(babase.Keyboard):
|
||||
# ba_meta export bauiv1.Keyboard
|
||||
class EnglishKeyboard(bui.Keyboard):
|
||||
"""Default English keyboard."""
|
||||
|
||||
name = 'English'
|
||||
|
||||
@ -501,9 +501,15 @@ class KioskWindow(bui.Window):
|
||||
def _do_full_menu(self) -> None:
|
||||
from bauiv1lib.mainmenu import MainMenuWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
assert bui.app.classic is not None
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
bui.app.classic.did_menu_intro = True # prevent delayed transition-in
|
||||
bui.app.ui_v1.set_main_menu_window(MainMenuWindow().get_root_widget())
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
MainMenuWindow().get_root_widget(), from_window=self._root_widget
|
||||
)
|
||||
|
||||
@ -1142,6 +1142,10 @@ class LeagueRankWindow(bui.Window):
|
||||
def _back(self) -> None:
|
||||
from bauiv1lib.coop.browser import CoopBrowserWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
@ -1149,5 +1153,6 @@ class LeagueRankWindow(bui.Window):
|
||||
if not self._modal:
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
CoopBrowserWindow(transition='in_left').get_root_widget()
|
||||
CoopBrowserWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
@ -1038,6 +1038,10 @@ class MainMenuWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.confirm import QuitWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
# Note: Normally we should go through bui.quit(confirm=True) but
|
||||
# invoking the window directly lets us scale it up from the
|
||||
# button.
|
||||
@ -1047,24 +1051,34 @@ class MainMenuWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.kiosk import KioskWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_right')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
KioskWindow(transition='in_left').get_root_widget()
|
||||
KioskWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _show_account_window(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.account.settings import AccountSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
AccountSettingsWindow(
|
||||
origin_widget=self._account_button
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _on_store_pressed(self) -> None:
|
||||
@ -1072,6 +1086,10 @@ class MainMenuWindow(bui.Window):
|
||||
from bauiv1lib.store.browser import StoreBrowserWindow
|
||||
from bauiv1lib.account import show_sign_in_prompt
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
@ -1084,7 +1102,8 @@ class MainMenuWindow(bui.Window):
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
StoreBrowserWindow(
|
||||
origin_widget=self._store_button
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _is_benchmark(self) -> bool:
|
||||
@ -1149,8 +1168,11 @@ class MainMenuWindow(bui.Window):
|
||||
|
||||
def _end_game(self) -> None:
|
||||
assert bui.app.classic is not None
|
||||
if not self._root_widget:
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
bui.app.classic.return_to_main_menu_session_gracefully(reset_ui=False)
|
||||
|
||||
@ -1166,39 +1188,54 @@ class MainMenuWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.creditslist import CreditsListWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
CreditsListWindow(
|
||||
origin_widget=self._credits_button
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _howtoplay(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.helpui import HelpWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
HelpWindow(
|
||||
main_menu=True, origin_widget=self._how_to_play_button
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _settings(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.allsettings import AllSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
AllSettingsWindow(
|
||||
origin_widget=self._settings_button
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _resume_and_call(self, call: Callable[[], Any]) -> None:
|
||||
@ -1281,35 +1318,50 @@ class MainMenuWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.gather import GatherWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
GatherWindow(origin_widget=self._gather_button).get_root_widget()
|
||||
GatherWindow(origin_widget=self._gather_button).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _watch_press(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.watch import WatchWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
WatchWindow(origin_widget=self._watch_button).get_root_widget()
|
||||
WatchWindow(origin_widget=self._watch_button).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _play_press(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.play import PlayWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.selecting_private_party_playlist = False
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PlayWindow(origin_widget=self._start_button).get_root_widget()
|
||||
PlayWindow(origin_widget=self._start_button).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _resume(self) -> None:
|
||||
|
||||
@ -93,9 +93,10 @@ class PartyWindow(bui.Window):
|
||||
iconscale=1.2,
|
||||
)
|
||||
|
||||
info = bs.get_connection_to_host_info()
|
||||
if info.get('name', '') != '':
|
||||
title = bui.Lstr(value=info['name'])
|
||||
info = bs.get_connection_to_host_info_2()
|
||||
|
||||
if info is not None and info.name != '':
|
||||
title = bui.Lstr(value=info.name)
|
||||
else:
|
||||
title = bui.Lstr(resource=self._r + '.titleText')
|
||||
|
||||
@ -483,7 +484,8 @@ class PartyWindow(bui.Window):
|
||||
kick_str = bui.Lstr(resource='kickText')
|
||||
else:
|
||||
# kick-votes appeared in build 14248
|
||||
if bs.get_connection_to_host_info().get('build_number', 0) < 14248:
|
||||
info = bs.get_connection_to_host_info_2()
|
||||
if info is None or info.build_number < 14248:
|
||||
return
|
||||
kick_str = bui.Lstr(resource='kickVoteText')
|
||||
assert bui.app.classic is not None
|
||||
|
||||
@ -521,13 +521,19 @@ class PlayWindow(bui.Window):
|
||||
|
||||
def _back(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
if self._is_main_menu:
|
||||
from bauiv1lib.mainmenu import MainMenuWindow
|
||||
|
||||
self._save_state()
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
MainMenuWindow(transition='in_left').get_root_widget()
|
||||
MainMenuWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
@ -538,7 +544,8 @@ class PlayWindow(bui.Window):
|
||||
self._save_state()
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
GatherWindow(transition='in_left').get_root_widget()
|
||||
GatherWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
@ -549,6 +556,10 @@ class PlayWindow(bui.Window):
|
||||
from bauiv1lib.account import show_sign_in_prompt
|
||||
from bauiv1lib.coop.browser import CoopBrowserWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
@ -559,26 +570,38 @@ class PlayWindow(bui.Window):
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
CoopBrowserWindow(origin_widget=self._coop_button).get_root_widget()
|
||||
CoopBrowserWindow(
|
||||
origin_widget=self._coop_button
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _team_tourney(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.playlist.browser import PlaylistBrowserWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PlaylistBrowserWindow(
|
||||
origin_widget=self._teams_button, sessiontype=bs.DualTeamSession
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _free_for_all(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.playlist.browser import PlaylistBrowserWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
@ -586,7 +609,8 @@ class PlayWindow(bui.Window):
|
||||
PlaylistBrowserWindow(
|
||||
origin_widget=self._free_for_all_button,
|
||||
sessiontype=bs.FreeForAllSession,
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _draw_dude(
|
||||
|
||||
@ -684,6 +684,10 @@ class PlaylistBrowserWindow(bui.Window):
|
||||
PlaylistCustomizeBrowserWindow,
|
||||
)
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
@ -691,13 +695,18 @@ class PlaylistBrowserWindow(bui.Window):
|
||||
PlaylistCustomizeBrowserWindow(
|
||||
origin_widget=self._customize_button,
|
||||
sessiontype=self._sessiontype,
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _on_back_press(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.play import PlayWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
# Store our selected playlist if that's changed.
|
||||
if self._selected_playlist is not None:
|
||||
prev_sel = bui.app.config.get(
|
||||
@ -716,7 +725,8 @@ class PlaylistBrowserWindow(bui.Window):
|
||||
)
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PlayWindow(transition='in_left').get_root_widget()
|
||||
PlayWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _save_state(self) -> None:
|
||||
|
||||
@ -323,6 +323,10 @@ class PlaylistCustomizeBrowserWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.playlist import browser
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
if self._selected_playlist_name is not None:
|
||||
cfg = bui.app.config
|
||||
cfg[
|
||||
@ -337,7 +341,8 @@ class PlaylistCustomizeBrowserWindow(bui.Window):
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
browser.PlaylistBrowserWindow(
|
||||
transition='in_left', sessiontype=self._sessiontype
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _select(self, name: str, index: int) -> None:
|
||||
|
||||
@ -283,6 +283,10 @@ class PlaylistEditWindow(bui.Window):
|
||||
PlaylistCustomizeBrowserWindow,
|
||||
)
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.getsound('powerdown01').play()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_right')
|
||||
assert bui.app.classic is not None
|
||||
@ -293,7 +297,8 @@ class PlaylistEditWindow(bui.Window):
|
||||
select_playlist=(
|
||||
self._editcontroller.get_existing_playlist_name()
|
||||
),
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _add(self) -> None:
|
||||
@ -315,6 +320,10 @@ class PlaylistEditWindow(bui.Window):
|
||||
PlaylistCustomizeBrowserWindow,
|
||||
)
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
@ -380,7 +389,8 @@ class PlaylistEditWindow(bui.Window):
|
||||
transition='in_left',
|
||||
sessiontype=self._editcontroller.get_session_type(),
|
||||
select_playlist=new_name,
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _save_press_with_sound(self) -> None:
|
||||
|
||||
@ -92,7 +92,8 @@ class PlaylistEditController:
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PlaylistEditWindow(
|
||||
editcontroller=self, transition=transition
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=False, # Disable this check.
|
||||
)
|
||||
|
||||
def get_config_name(self) -> str:
|
||||
@ -150,7 +151,8 @@ class PlaylistEditController:
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.clear_main_menu_window(transition='out_left')
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PlaylistAddGameWindow(editcontroller=self).get_root_widget()
|
||||
PlaylistAddGameWindow(editcontroller=self).get_root_widget(),
|
||||
from_window=None,
|
||||
)
|
||||
|
||||
def edit_game_pressed(self) -> None:
|
||||
@ -175,7 +177,8 @@ class PlaylistEditController:
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PlaylistEditWindow(
|
||||
editcontroller=self, transition='in_left'
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=None,
|
||||
)
|
||||
|
||||
def _show_edit_ui(
|
||||
@ -205,7 +208,8 @@ class PlaylistEditController:
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PlaylistEditWindow(
|
||||
editcontroller=self, transition='in_left'
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=None,
|
||||
)
|
||||
|
||||
# Otherwise we were adding; go back to the add type choice list.
|
||||
@ -214,7 +218,8 @@ class PlaylistEditController:
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PlaylistAddGameWindow(
|
||||
editcontroller=self, transition='in_left'
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=None,
|
||||
)
|
||||
else:
|
||||
# Make sure type is in there.
|
||||
@ -236,5 +241,6 @@ class PlaylistEditController:
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PlaylistEditWindow(
|
||||
editcontroller=self, transition='in_left'
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=None,
|
||||
)
|
||||
|
||||
@ -514,6 +514,10 @@ class PlaylistEditGameWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.playlist.mapselect import PlaylistMapSelectWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
# Replace ourself with the map-select UI.
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
@ -524,7 +528,8 @@ class PlaylistEditGameWindow(bui.Window):
|
||||
copy.deepcopy(self._getconfig()),
|
||||
self._edit_info,
|
||||
self._completion_call,
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _choice_inc(
|
||||
|
||||
@ -273,6 +273,10 @@ class PlaylistMapSelectWindow(bui.Window):
|
||||
def _select(self, map_name: str) -> None:
|
||||
from bauiv1lib.playlist.editgame import PlaylistEditGameWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._config['settings']['map'] = map_name
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_right')
|
||||
assert bui.app.classic is not None
|
||||
@ -285,7 +289,8 @@ class PlaylistMapSelectWindow(bui.Window):
|
||||
default_selection='map',
|
||||
transition='in_left',
|
||||
edit_info=self._edit_info,
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _select_with_delay(self, map_name: str) -> None:
|
||||
@ -296,6 +301,10 @@ class PlaylistMapSelectWindow(bui.Window):
|
||||
def _cancel(self) -> None:
|
||||
from bauiv1lib.playlist.editgame import PlaylistEditGameWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_right')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
@ -307,5 +316,6 @@ class PlaylistMapSelectWindow(bui.Window):
|
||||
default_selection='map',
|
||||
transition='in_left',
|
||||
edit_info=self._edit_info,
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
@ -140,7 +140,6 @@ class PlayOptionsWindow(PopupWindow):
|
||||
if show_shuffle_check_box:
|
||||
self._height += 40
|
||||
|
||||
# Creates our _root_widget.
|
||||
uiscale = bui.app.ui_v1.uiscale
|
||||
scale = (
|
||||
1.69
|
||||
@ -149,6 +148,7 @@ class PlayOptionsWindow(PopupWindow):
|
||||
if uiscale is bui.UIScale.MEDIUM
|
||||
else 0.85
|
||||
)
|
||||
# Creates our _root_widget.
|
||||
super().__init__(
|
||||
position=scale_origin, size=(self._width, self._height), scale=scale
|
||||
)
|
||||
@ -448,6 +448,10 @@ class PlayOptionsWindow(PopupWindow):
|
||||
self._transition_out()
|
||||
|
||||
def _on_ok_press(self) -> None:
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self.root_widget or self.root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
# Disallow if our playlist has disappeared.
|
||||
if not self._does_target_playlist_exist():
|
||||
return
|
||||
@ -478,8 +482,12 @@ class PlayOptionsWindow(PopupWindow):
|
||||
cfg['Private Party Host Session Type'] = typename
|
||||
bui.getsound('gunCocking').play()
|
||||
assert bui.app.classic is not None
|
||||
# Note: this is a wonky situation where we aren't actually
|
||||
# the main window but we set it on behalf of the main window
|
||||
# that popped us up.
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
GatherWindow(transition='in_right').get_root_widget()
|
||||
GatherWindow(transition='in_right').get_root_widget(),
|
||||
from_window=False, # Disable this test.
|
||||
)
|
||||
self._transition_out(transition='out_left')
|
||||
if self._delegate is not None:
|
||||
|
||||
@ -214,6 +214,10 @@ class ProfileBrowserWindow(bui.Window):
|
||||
from bauiv1lib.profile.edit import EditProfileWindow
|
||||
from bauiv1lib.purchase import PurchaseWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
@ -254,7 +258,8 @@ class ProfileBrowserWindow(bui.Window):
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
EditProfileWindow(
|
||||
existing_profile=None, in_main_menu=self._in_main_menu
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget if self._in_main_menu else False,
|
||||
)
|
||||
|
||||
def _delete_profile(self) -> None:
|
||||
@ -303,6 +308,10 @@ class ProfileBrowserWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.profile.edit import EditProfileWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
if self._selected_profile is None:
|
||||
bui.getsound('error').play()
|
||||
bui.screenmessage(
|
||||
@ -315,7 +324,8 @@ class ProfileBrowserWindow(bui.Window):
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
EditProfileWindow(
|
||||
self._selected_profile, in_main_menu=self._in_main_menu
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget if self._in_main_menu else False,
|
||||
)
|
||||
|
||||
def _select(self, name: str, index: int) -> None:
|
||||
@ -326,6 +336,10 @@ class ProfileBrowserWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.account.settings import AccountSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
assert bui.app.classic is not None
|
||||
|
||||
self._save_state()
|
||||
@ -335,7 +349,8 @@ class ProfileBrowserWindow(bui.Window):
|
||||
if self._in_main_menu:
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
AccountSettingsWindow(transition='in_left').get_root_widget()
|
||||
AccountSettingsWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
# If we're being called up standalone, handle pause/resume ourself.
|
||||
|
||||
@ -18,12 +18,18 @@ class EditProfileWindow(bui.Window):
|
||||
# FIXME: WILL NEED TO CHANGE THIS FOR UILOCATION.
|
||||
def reload_window(self) -> None:
|
||||
"""Transitions out and recreates ourself."""
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
EditProfileWindow(
|
||||
self.getname(), self._in_main_menu
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def __init__(
|
||||
@ -672,6 +678,10 @@ class EditProfileWindow(bui.Window):
|
||||
def _cancel(self) -> None:
|
||||
from bauiv1lib.profile.browser import ProfileBrowserWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_right')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
@ -679,7 +689,8 @@ class EditProfileWindow(bui.Window):
|
||||
'in_left',
|
||||
selected_profile=self._existing_profile,
|
||||
in_main_menu=self._in_main_menu,
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _set_color(self, color: tuple[float, float, float]) -> None:
|
||||
@ -778,6 +789,10 @@ class EditProfileWindow(bui.Window):
|
||||
"""Save has been selected."""
|
||||
from bauiv1lib.profile.browser import ProfileBrowserWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return False
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
@ -827,6 +842,7 @@ class EditProfileWindow(bui.Window):
|
||||
'in_left',
|
||||
selected_profile=new_name,
|
||||
in_main_menu=self._in_main_menu,
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
return True
|
||||
|
||||
@ -142,13 +142,18 @@ class PromoCodeWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.advanced import AdvancedSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
)
|
||||
if not self._modal:
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
AdvancedSettingsWindow(transition='in_left').get_root_widget()
|
||||
AdvancedSettingsWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _activate_enter_button(self) -> None:
|
||||
@ -158,6 +163,10 @@ class PromoCodeWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.advanced import AdvancedSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
@ -167,7 +176,8 @@ class PromoCodeWindow(bui.Window):
|
||||
if not self._modal:
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
AdvancedSettingsWindow(transition='in_left').get_root_widget()
|
||||
AdvancedSettingsWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
plus.add_v1_account_transaction(
|
||||
{
|
||||
|
||||
@ -682,11 +682,16 @@ class AdvancedSettingsWindow(bui.Window):
|
||||
def _on_vr_test_press(self) -> None:
|
||||
from bauiv1lib.settings.vrtesting import VRTestingWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
VRTestingWindow(transition='in_right').get_root_widget()
|
||||
VRTestingWindow(transition='in_right').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _on_net_test_press(self) -> None:
|
||||
@ -694,6 +699,10 @@ class AdvancedSettingsWindow(bui.Window):
|
||||
assert plus is not None
|
||||
from bauiv1lib.settings.nettesting import NetTestingWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
# Net-testing requires a signed in v1 account.
|
||||
if plus.get_v1_account_state() != 'signed_in':
|
||||
bui.screenmessage(
|
||||
@ -706,7 +715,8 @@ class AdvancedSettingsWindow(bui.Window):
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
NetTestingWindow(transition='in_right').get_root_widget()
|
||||
NetTestingWindow(transition='in_right').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _on_friend_promo_code_press(self) -> None:
|
||||
@ -724,17 +734,26 @@ class AdvancedSettingsWindow(bui.Window):
|
||||
def _on_plugins_button_press(self) -> None:
|
||||
from bauiv1lib.settings.plugins import PluginWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PluginWindow(origin_widget=self._plugins_button).get_root_widget()
|
||||
PluginWindow(origin_widget=self._plugins_button).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _on_promo_code_press(self) -> None:
|
||||
from bauiv1lib.promocode import PromoCodeWindow
|
||||
from bauiv1lib.account import show_sign_in_prompt
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
@ -742,23 +761,30 @@ class AdvancedSettingsWindow(bui.Window):
|
||||
if plus.get_v1_account_state() != 'signed_in':
|
||||
show_sign_in_prompt()
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PromoCodeWindow(
|
||||
origin_widget=self._promo_code_button
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _on_benchmark_press(self) -> None:
|
||||
from bauiv1lib.debug import DebugWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
DebugWindow(transition='in_right').get_root_widget()
|
||||
DebugWindow(transition='in_right').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _save_state(self) -> None:
|
||||
@ -908,11 +934,16 @@ class AdvancedSettingsWindow(bui.Window):
|
||||
def _do_back(self) -> None:
|
||||
from bauiv1lib.settings.allsettings import AllSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
)
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
AllSettingsWindow(transition='in_left').get_root_widget()
|
||||
AllSettingsWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
@ -235,65 +235,90 @@ class AllSettingsWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.mainmenu import MainMenuWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
)
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
MainMenuWindow(transition='in_left').get_root_widget()
|
||||
MainMenuWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _do_controllers(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.controls import ControlsSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
ControlsSettingsWindow(
|
||||
origin_widget=self._controllers_button
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _do_graphics(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.graphics import GraphicsSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
GraphicsSettingsWindow(
|
||||
origin_widget=self._graphics_button
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _do_audio(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.audio import AudioSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
AudioSettingsWindow(
|
||||
origin_widget=self._audio_button
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _do_advanced(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.advanced import AdvancedSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
AdvancedSettingsWindow(
|
||||
origin_widget=self._advanced_button
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _save_state(self) -> None:
|
||||
|
||||
@ -237,6 +237,10 @@ class AudioSettingsWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.soundtrack import browser as stb
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
# We require disk access for soundtracks;
|
||||
# if we don't have it, request it.
|
||||
if not bui.have_permission(bui.Permission.STORAGE):
|
||||
@ -256,13 +260,18 @@ class AudioSettingsWindow(bui.Window):
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
stb.SoundtrackBrowserWindow(
|
||||
origin_widget=self._soundtrack_button
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _back(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings import allsettings
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
@ -271,7 +280,8 @@ class AudioSettingsWindow(bui.Window):
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
allsettings.AllSettingsWindow(
|
||||
transition='in_left'
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _save_state(self) -> None:
|
||||
|
||||
@ -367,59 +367,84 @@ class ControlsSettingsWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.keyboard import ConfigKeyboardWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
ConfigKeyboardWindow(
|
||||
bs.getinputdevice('Keyboard', '#1')
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _config_keyboard2(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.keyboard import ConfigKeyboardWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
ConfigKeyboardWindow(
|
||||
bs.getinputdevice('Keyboard', '#2')
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _do_mobile_devices(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.remoteapp import RemoteAppSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
RemoteAppSettingsWindow().get_root_widget()
|
||||
RemoteAppSettingsWindow().get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _do_gamepads(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.gamepadselect import GamepadSelectWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
GamepadSelectWindow().get_root_widget()
|
||||
GamepadSelectWindow().get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _do_touchscreen(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.touchscreen import TouchscreenSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
TouchscreenSettingsWindow().get_root_widget()
|
||||
TouchscreenSettingsWindow().get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _save_state(self) -> None:
|
||||
@ -466,11 +491,16 @@ class ControlsSettingsWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.allsettings import AllSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
)
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
AllSettingsWindow(transition='in_left').get_root_widget()
|
||||
AllSettingsWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
@ -795,19 +795,28 @@ class GamepadSettingsWindow(bui.Window):
|
||||
def _cancel(self) -> None:
|
||||
from bauiv1lib.settings.controls import ControlsSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
)
|
||||
if self._is_main_menu:
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
ControlsSettingsWindow(transition='in_left').get_root_widget()
|
||||
ControlsSettingsWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _save(self) -> None:
|
||||
classic = bui.app.classic
|
||||
assert classic is not None
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
)
|
||||
@ -852,7 +861,8 @@ class GamepadSettingsWindow(bui.Window):
|
||||
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
ControlsSettingsWindow(transition='in_left').get_root_widget()
|
||||
ControlsSettingsWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@ -33,7 +33,8 @@ def gamepad_configure_callback(event: dict[str, Any]) -> None:
|
||||
assert isinstance(device, bs.InputDevice)
|
||||
if device.allows_configuring:
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
gamepad.GamepadSettingsWindow(device).get_root_widget()
|
||||
gamepad.GamepadSettingsWindow(device).get_root_widget(),
|
||||
from_window=None,
|
||||
)
|
||||
else:
|
||||
width = 700
|
||||
@ -51,7 +52,7 @@ def gamepad_configure_callback(event: dict[str, Any]) -> None:
|
||||
size=(width, height),
|
||||
transition='in_right',
|
||||
)
|
||||
bui.app.ui_v1.set_main_menu_window(dlg)
|
||||
bui.app.ui_v1.set_main_menu_window(dlg, from_window=None)
|
||||
|
||||
if device.allows_configuring_in_system_settings:
|
||||
msg = bui.Lstr(
|
||||
@ -81,12 +82,17 @@ def gamepad_configure_callback(event: dict[str, Any]) -> None:
|
||||
def _ok() -> None:
|
||||
from bauiv1lib.settings import controls
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not dlg or dlg.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(edit=dlg, transition='out_right')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
controls.ControlsSettingsWindow(
|
||||
transition='in_left'
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=dlg,
|
||||
)
|
||||
|
||||
bui.buttonwidget(
|
||||
@ -191,11 +197,16 @@ class GamepadSelectWindow(bui.Window):
|
||||
def _back(self) -> None:
|
||||
from bauiv1lib.settings import controls
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bs.release_gamepad_input()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_right')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
controls.ControlsSettingsWindow(
|
||||
transition='in_left'
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
@ -436,6 +436,10 @@ class GraphicsSettingsWindow(bui.Window):
|
||||
def _back(self) -> None:
|
||||
from bauiv1lib.settings import allsettings
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
# Applying max-fps takes a few moments. Apply if it hasn't been
|
||||
# yet.
|
||||
self._apply_max_fps()
|
||||
@ -447,7 +451,8 @@ class GraphicsSettingsWindow(bui.Window):
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
allsettings.AllSettingsWindow(
|
||||
transition='in_left'
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _set_quality(self, quality: str) -> None:
|
||||
|
||||
@ -271,15 +271,24 @@ class ConfigKeyboardWindow(bui.Window):
|
||||
def _cancel(self) -> None:
|
||||
from bauiv1lib.settings.controls import ControlsSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_right')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
ControlsSettingsWindow(transition='in_left').get_root_widget()
|
||||
ControlsSettingsWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _save(self) -> None:
|
||||
from bauiv1lib.settings.controls import ControlsSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
assert bui.app.classic is not None
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_right')
|
||||
bui.getsound('gunCocking').play()
|
||||
@ -314,7 +323,8 @@ class ConfigKeyboardWindow(bui.Window):
|
||||
)
|
||||
bui.app.config.apply_and_commit()
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
ControlsSettingsWindow(transition='in_left').get_root_widget()
|
||||
ControlsSettingsWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@ -135,8 +135,14 @@ class NetTestingWindow(bui.Window):
|
||||
|
||||
def _show_val_testing(self) -> None:
|
||||
assert bui.app.classic is not None
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
NetValTestingWindow().get_root_widget()
|
||||
NetValTestingWindow().get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
|
||||
@ -144,9 +150,14 @@ class NetTestingWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.advanced import AdvancedSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
AdvancedSettingsWindow(transition='in_left').get_root_widget()
|
||||
AdvancedSettingsWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_right')
|
||||
|
||||
|
||||
@ -232,11 +232,16 @@ class PluginWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.pluginsettings import PluginSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PluginSettingsWindow(transition='in_right').get_root_widget()
|
||||
PluginSettingsWindow(transition='in_right').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _show_category_options(self) -> None:
|
||||
@ -449,11 +454,16 @@ class PluginWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.advanced import AdvancedSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
)
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
AdvancedSettingsWindow(transition='in_left').get_root_widget()
|
||||
AdvancedSettingsWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
@ -161,10 +161,15 @@ class PluginSettingsWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.plugins import PluginWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
)
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PluginWindow(transition='in_left').get_root_widget()
|
||||
PluginWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
@ -138,10 +138,15 @@ class RemoteAppSettingsWindow(bui.Window):
|
||||
def _back(self) -> None:
|
||||
from bauiv1lib.settings import controls
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_right')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
controls.ControlsSettingsWindow(
|
||||
transition='in_left'
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
@ -217,6 +217,10 @@ class TestingWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.advanced import AdvancedSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_right')
|
||||
backwin = (
|
||||
self._back_call()
|
||||
@ -224,4 +228,6 @@ class TestingWindow(bui.Window):
|
||||
else AdvancedSettingsWindow(transition='in_left')
|
||||
)
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(backwin.get_root_widget())
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
backwin.get_root_widget(), from_window=self._root_widget
|
||||
)
|
||||
|
||||
@ -276,11 +276,16 @@ class TouchscreenSettingsWindow(bui.Window):
|
||||
def _back(self) -> None:
|
||||
from bauiv1lib.settings import controls
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_right')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
controls.ControlsSettingsWindow(
|
||||
transition='in_left'
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
bs.set_touchscreen_editing(False)
|
||||
|
||||
@ -394,13 +394,18 @@ class SoundtrackBrowserWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings import audio
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
)
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
audio.AudioSettingsWindow(transition='in_left').get_root_widget()
|
||||
audio.AudioSettingsWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _edit_soundtrack_with_sound(self) -> None:
|
||||
@ -421,6 +426,10 @@ class SoundtrackBrowserWindow(bui.Window):
|
||||
from bauiv1lib.purchase import PurchaseWindow
|
||||
from bauiv1lib.soundtrack.edit import SoundtrackEditWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
if (
|
||||
bui.app.classic is not None
|
||||
and not bui.app.classic.accounts.have_pro_options()
|
||||
@ -443,7 +452,8 @@ class SoundtrackBrowserWindow(bui.Window):
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
SoundtrackEditWindow(
|
||||
existing_soundtrack=self._selected_soundtrack
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _get_soundtrack_display_name(self, soundtrack: str) -> bui.Lstr:
|
||||
|
||||
@ -351,7 +351,8 @@ class SoundtrackEditWindow(bui.Window):
|
||||
soundtrack[musictype] = entry
|
||||
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
cls(state, transition='in_left').get_root_widget()
|
||||
cls(state, transition='in_left').get_root_widget(),
|
||||
from_window=False, # Disable check here.
|
||||
)
|
||||
|
||||
def _get_entry(
|
||||
@ -359,6 +360,11 @@ class SoundtrackEditWindow(bui.Window):
|
||||
) -> None:
|
||||
assert bui.app.classic is not None
|
||||
music = bui.app.classic.music
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
if selection_target_name != '':
|
||||
selection_target_name = "'" + selection_target_name + "'"
|
||||
state = {
|
||||
@ -375,7 +381,8 @@ class SoundtrackEditWindow(bui.Window):
|
||||
entry,
|
||||
selection_target_name,
|
||||
)
|
||||
.get_root_widget()
|
||||
.get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _test(self, song_type: bs.MusicType) -> None:
|
||||
@ -422,6 +429,10 @@ class SoundtrackEditWindow(bui.Window):
|
||||
def _cancel(self) -> None:
|
||||
from bauiv1lib.soundtrack import browser as stb
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
assert bui.app.classic is not None
|
||||
music = bui.app.classic.music
|
||||
|
||||
@ -429,12 +440,17 @@ class SoundtrackEditWindow(bui.Window):
|
||||
music.set_music_play_mode(bui.app.classic.MusicPlayMode.REGULAR)
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_right')
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
stb.SoundtrackBrowserWindow(transition='in_left').get_root_widget()
|
||||
stb.SoundtrackBrowserWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _do_it(self) -> None:
|
||||
from bauiv1lib.soundtrack import browser as stb
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
assert bui.app.classic is not None
|
||||
music = bui.app.classic.music
|
||||
cfg = bui.app.config
|
||||
@ -483,7 +499,8 @@ class SoundtrackEditWindow(bui.Window):
|
||||
)
|
||||
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
stb.SoundtrackBrowserWindow(transition='in_left').get_root_widget()
|
||||
stb.SoundtrackBrowserWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _do_it_with_sound(self) -> None:
|
||||
|
||||
@ -166,6 +166,10 @@ class SoundtrackEntryTypeSelectWindow(bui.Window):
|
||||
MacMusicAppPlaylistSelectWindow,
|
||||
)
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
|
||||
current_playlist_entry: str | None
|
||||
@ -181,7 +185,8 @@ class SoundtrackEntryTypeSelectWindow(bui.Window):
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
MacMusicAppPlaylistSelectWindow(
|
||||
self._callback, current_playlist_entry, self._current_entry
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _on_music_file_press(self) -> None:
|
||||
@ -189,6 +194,10 @@ class SoundtrackEntryTypeSelectWindow(bui.Window):
|
||||
from baclassic.osmusic import OSMusicPlayer
|
||||
from bauiv1lib.fileselector import FileSelectorWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
base_path = android_get_external_files_dir()
|
||||
assert bui.app.classic is not None
|
||||
@ -201,13 +210,18 @@ class SoundtrackEntryTypeSelectWindow(bui.Window):
|
||||
OSMusicPlayer.get_valid_music_file_extensions()
|
||||
),
|
||||
allow_folders=False,
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _on_music_folder_press(self) -> None:
|
||||
from bauiv1lib.fileselector import FileSelectorWindow
|
||||
from babase import android_get_external_files_dir
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
base_path = android_get_external_files_dir()
|
||||
assert bui.app.classic is not None
|
||||
@ -218,7 +232,8 @@ class SoundtrackEntryTypeSelectWindow(bui.Window):
|
||||
show_base_path=False,
|
||||
valid_file_extensions=[],
|
||||
allow_folders=True,
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _music_file_selector_cb(self, result: str | None) -> None:
|
||||
|
||||
@ -551,9 +551,11 @@ def show_offer() -> bool:
|
||||
if bui.native_review_request_supported():
|
||||
bui.native_review_request()
|
||||
else:
|
||||
feedback.ask_for_rating()
|
||||
if app.ui_v1.available:
|
||||
feedback.ask_for_rating()
|
||||
else:
|
||||
SpecialOfferWindow(app.classic.special_offer)
|
||||
if app.ui_v1.available:
|
||||
SpecialOfferWindow(app.classic.special_offer)
|
||||
|
||||
app.classic.special_offer = None
|
||||
return True
|
||||
|
||||
@ -1329,6 +1329,10 @@ class StoreBrowserWindow(bui.Window):
|
||||
from bauiv1lib.account import show_sign_in_prompt
|
||||
from bauiv1lib.getcurrency import GetCurrencyWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
@ -1343,13 +1347,19 @@ class StoreBrowserWindow(bui.Window):
|
||||
).get_root_widget()
|
||||
if not self._modal:
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(window)
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
window, from_window=self._root_widget
|
||||
)
|
||||
|
||||
def _back(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.coop.browser import CoopBrowserWindow
|
||||
from bauiv1lib.mainmenu import MainMenuWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
@ -1358,11 +1368,13 @@ class StoreBrowserWindow(bui.Window):
|
||||
assert bui.app.classic is not None
|
||||
if self._back_location == 'CoopBrowserWindow':
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
CoopBrowserWindow(transition='in_left').get_root_widget()
|
||||
CoopBrowserWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
else:
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
MainMenuWindow(transition='in_left').get_root_widget()
|
||||
MainMenuWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
if self._on_close_call is not None:
|
||||
self._on_close_call()
|
||||
|
||||
@ -34,6 +34,7 @@ class TournamentEntryWindow(PopupWindow):
|
||||
# pylint: disable=too-many-statements
|
||||
|
||||
assert bui.app.classic is not None
|
||||
assert bui.app.plus
|
||||
bui.set_analytics_screen('Tournament Entry Window')
|
||||
|
||||
self._tournament_id = tournament_id
|
||||
@ -100,7 +101,7 @@ class TournamentEntryWindow(PopupWindow):
|
||||
self._launched = False
|
||||
|
||||
# Show the ad button only if we support ads *and* it has a level 1 fee.
|
||||
self._do_ad_btn = bui.has_video_ads() and self._allow_ads
|
||||
self._do_ad_btn = bui.app.plus.has_video_ads() and self._allow_ads
|
||||
|
||||
x_offs = 0 if self._do_ad_btn else 85
|
||||
|
||||
@ -477,7 +478,7 @@ class TournamentEntryWindow(PopupWindow):
|
||||
)
|
||||
|
||||
if self._do_ad_btn:
|
||||
enabled = bui.have_incentivized_ad()
|
||||
enabled = plus.have_incentivized_ad()
|
||||
have_ad_tries_remaining = (
|
||||
self._tournament_info['adTriesRemaining'] is not None
|
||||
and self._tournament_info['adTriesRemaining'] > 0
|
||||
|
||||
@ -663,11 +663,16 @@ class WatchWindow(bui.Window):
|
||||
def _back(self) -> None:
|
||||
from bauiv1lib.mainmenu import MainMenuWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
)
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
MainMenuWindow(transition='in_left').get_root_widget()
|
||||
MainMenuWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
@ -25,138 +25,13 @@ void AppAdapter::OnMainThreadStartApp() {
|
||||
}
|
||||
|
||||
void AppAdapter::OnAppStart() { assert(g_base->InLogicThread()); }
|
||||
void AppAdapter::OnAppPause() { assert(g_base->InLogicThread()); }
|
||||
void AppAdapter::OnAppResume() { assert(g_base->InLogicThread()); }
|
||||
void AppAdapter::OnAppSuspend() { assert(g_base->InLogicThread()); }
|
||||
void AppAdapter::OnAppUnsuspend() { assert(g_base->InLogicThread()); }
|
||||
void AppAdapter::OnAppShutdown() { assert(g_base->InLogicThread()); }
|
||||
void AppAdapter::OnAppShutdownComplete() { assert(g_base->InLogicThread()); }
|
||||
void AppAdapter::OnScreenSizeChange() { assert(g_base->InLogicThread()); }
|
||||
void AppAdapter::DoApplyAppConfig() { assert(g_base->InLogicThread()); }
|
||||
|
||||
void AppAdapter::OnAppSuspend_() {
|
||||
assert(g_core->InMainThread());
|
||||
|
||||
// IMPORTANT: Any pause related stuff that event-loop-threads need to do
|
||||
// should be done from their registered pause-callbacks. If we instead
|
||||
// push runnables to them from here they may or may not be called before
|
||||
// their event-loop is actually paused.
|
||||
|
||||
// Pause all event loops.
|
||||
EventLoop::SetEventLoopsSuspended(true);
|
||||
|
||||
if (g_base->network_reader) {
|
||||
g_base->network_reader->OnAppPause();
|
||||
}
|
||||
g_base->networking->OnAppPause();
|
||||
}
|
||||
|
||||
void AppAdapter::OnAppUnsuspend_() {
|
||||
assert(g_core->InMainThread());
|
||||
|
||||
// Spin all event-loops back up.
|
||||
EventLoop::SetEventLoopsSuspended(false);
|
||||
|
||||
// Run resumes that expect to happen in the main thread.
|
||||
g_base->network_reader->OnAppResume();
|
||||
g_base->networking->OnAppResume();
|
||||
|
||||
// When resuming from a suspended state, we may want to pause whatever
|
||||
// game was running when we last were active.
|
||||
//
|
||||
// TODO(efro): we should make this smarter so it doesn't happen if we're
|
||||
// in a network game or something that we can't pause; bringing up the
|
||||
// menu doesn't really accomplish anything there.
|
||||
//
|
||||
// In general this probably should be handled at a higher level.
|
||||
// if (g_core->should_pause_active_game) {
|
||||
// g_core->should_pause_active_game = false;
|
||||
|
||||
// // If we've been completely backgrounded, send a menu-press command to
|
||||
// // the game; this will bring up a pause menu if we're in the game/etc.
|
||||
// if (!g_base->ui->MainMenuVisible()) {
|
||||
// g_base->ui->PushMainMenuPressCall(nullptr);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
void AppAdapter::SuspendApp() {
|
||||
assert(g_core);
|
||||
assert(g_core->InMainThread());
|
||||
|
||||
if (app_suspended_) {
|
||||
Log(LogLevel::kWarning,
|
||||
"AppAdapter::SuspendApp() called with app already suspended.");
|
||||
return;
|
||||
}
|
||||
|
||||
millisecs_t start_time{core::CorePlatform::GetCurrentMillisecs()};
|
||||
|
||||
// Apple mentioned 5 seconds to run stuff once backgrounded or they bring
|
||||
// down the hammer. Let's aim to stay under 2.
|
||||
millisecs_t max_duration{2000};
|
||||
|
||||
g_core->platform->DebugLog(
|
||||
"SuspendApp@"
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs()));
|
||||
app_suspended_ = true;
|
||||
OnAppSuspend_();
|
||||
|
||||
// We assume that the OS will completely suspend our process the moment we
|
||||
// return from this call (though this is not technically true on all
|
||||
// platforms). So we want to spin and wait for threads to actually process
|
||||
// the pause message.
|
||||
size_t running_thread_count{};
|
||||
while (std::abs(core::CorePlatform::GetCurrentMillisecs() - start_time)
|
||||
< max_duration) {
|
||||
// If/when we get to a point with no threads waiting to be paused, we're
|
||||
// good to go.
|
||||
auto threads{EventLoop::GetStillSuspendingEventLoops()};
|
||||
running_thread_count = threads.size();
|
||||
if (running_thread_count == 0) {
|
||||
if (g_buildconfig.debug_build()) {
|
||||
Log(LogLevel::kDebug,
|
||||
"SuspendApp() completed in "
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs()
|
||||
- start_time)
|
||||
+ "ms.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If we made it here, we timed out. Complain.
|
||||
Log(LogLevel::kError,
|
||||
std::string("SuspendApp() took too long; ")
|
||||
+ std::to_string(running_thread_count)
|
||||
+ " threads not yet paused after "
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs()
|
||||
- start_time)
|
||||
+ " ms.");
|
||||
}
|
||||
|
||||
void AppAdapter::UnsuspendApp() {
|
||||
assert(g_core);
|
||||
assert(g_core->InMainThread());
|
||||
|
||||
if (!app_suspended_) {
|
||||
Log(LogLevel::kWarning,
|
||||
"AppAdapter::UnsuspendApp() called with app not in suspendedstate.");
|
||||
return;
|
||||
}
|
||||
millisecs_t start_time{core::CorePlatform::GetCurrentMillisecs()};
|
||||
g_core->platform->DebugLog(
|
||||
"UnsuspendApp@"
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs()));
|
||||
app_suspended_ = false;
|
||||
OnAppUnsuspend_();
|
||||
if (g_buildconfig.debug_build()) {
|
||||
Log(LogLevel::kDebug,
|
||||
"UnsuspendApp() completed in "
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs()
|
||||
- start_time)
|
||||
+ "ms.");
|
||||
}
|
||||
}
|
||||
|
||||
void AppAdapter::RunMainThreadEventLoopToCompletion() {
|
||||
FatalError("RunMainThreadEventLoopToCompletion is not implemented here.");
|
||||
}
|
||||
@ -242,41 +117,6 @@ auto AppAdapter::GetGraphicsClientContext() -> GraphicsClientContext* {
|
||||
auto AppAdapter::GetKeyRepeatDelay() -> float { return 0.3f; }
|
||||
auto AppAdapter::GetKeyRepeatInterval() -> float { return 0.08f; }
|
||||
|
||||
auto AppAdapter::ClipboardIsSupported() -> bool {
|
||||
// We only call our actual virtual function once.
|
||||
if (!have_clipboard_is_supported_) {
|
||||
clipboard_is_supported_ = DoClipboardIsSupported();
|
||||
have_clipboard_is_supported_ = true;
|
||||
}
|
||||
return clipboard_is_supported_;
|
||||
}
|
||||
|
||||
auto AppAdapter::ClipboardHasText() -> bool {
|
||||
// If subplatform says they don't support clipboards, don't even ask.
|
||||
if (!ClipboardIsSupported()) {
|
||||
return false;
|
||||
}
|
||||
return DoClipboardHasText();
|
||||
}
|
||||
|
||||
void AppAdapter::ClipboardSetText(const std::string& text) {
|
||||
// If subplatform says they don't support clipboards, this is an error.
|
||||
if (!ClipboardIsSupported()) {
|
||||
throw Exception("ClipboardSetText called with no clipboard support.",
|
||||
PyExcType::kRuntime);
|
||||
}
|
||||
DoClipboardSetText(text);
|
||||
}
|
||||
|
||||
auto AppAdapter::ClipboardGetText() -> std::string {
|
||||
// If subplatform says they don't support clipboards, this is an error.
|
||||
if (!ClipboardIsSupported()) {
|
||||
throw Exception("ClipboardGetText called with no clipboard support.",
|
||||
PyExcType::kRuntime);
|
||||
}
|
||||
return DoClipboardGetText();
|
||||
}
|
||||
|
||||
auto AppAdapter::DoClipboardIsSupported() -> bool { return false; }
|
||||
|
||||
auto AppAdapter::DoClipboardHasText() -> bool {
|
||||
@ -311,4 +151,6 @@ void AppAdapter::NativeReviewRequest() {
|
||||
|
||||
void AppAdapter::DoNativeReviewRequest() { FatalError("Fixme unimplemented."); }
|
||||
|
||||
auto AppAdapter::ShouldSilenceAudioForInactive() -> bool const { return false; }
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
@ -22,8 +22,8 @@ class AppAdapter {
|
||||
|
||||
// Logic thread callbacks.
|
||||
virtual void OnAppStart();
|
||||
virtual void OnAppPause();
|
||||
virtual void OnAppResume();
|
||||
virtual void OnAppSuspend();
|
||||
virtual void OnAppUnsuspend();
|
||||
virtual void OnAppShutdown();
|
||||
virtual void OnAppShutdownComplete();
|
||||
virtual void OnScreenSizeChange();
|
||||
@ -88,9 +88,9 @@ class AppAdapter {
|
||||
/// plugged in or unplugged/etc. Default implementation returns true.
|
||||
virtual auto ShouldUseCursor() -> bool;
|
||||
|
||||
/// Return whether the app-adapter is having the OS show a cursor.
|
||||
/// If this returns false, the engine will take care of drawing a cursor
|
||||
/// when necessary. If true, SetHardwareCursorVisible will be called
|
||||
/// Return whether the app-adapter is having the OS show a cursor. If this
|
||||
/// returns false, the engine will take care of drawing a cursor when
|
||||
/// necessary. If true, SetHardwareCursorVisible will be called
|
||||
/// periodically to inform the adapter what the cursor state should be.
|
||||
/// The default implementation returns false;
|
||||
virtual auto HasHardwareCursor() -> bool;
|
||||
@ -106,21 +106,6 @@ class AppAdapter {
|
||||
/// values.
|
||||
virtual void CursorPositionForDraw(float* x, float* y);
|
||||
|
||||
/// Put the app into a suspended state. Should be called from the main
|
||||
/// thread. Pauses work, closes network sockets, etc. May correspond to
|
||||
/// being backgrounded on mobile, being minimized on desktop, etc. It is
|
||||
/// assumed that, as soon as this call returns, all work is finished and
|
||||
/// all threads can be suspended by the OS without any negative side
|
||||
/// effects.
|
||||
void SuspendApp();
|
||||
|
||||
/// Return the app to a running state from a suspended one. Can correspond
|
||||
/// to foregrounding on mobile, unminimizing on desktop, etc. Spins
|
||||
/// threads back up, re-opens network sockets, etc.
|
||||
void UnsuspendApp();
|
||||
|
||||
auto app_suspended() const { return app_suspended_; }
|
||||
|
||||
/// Return whether this AppAdapter supports a 'fullscreen' toggle for its
|
||||
/// display. This will affect whether that option is available in display
|
||||
/// settings or via a hotkey. Must be called from the logic thread.
|
||||
@ -150,6 +135,15 @@ class AppAdapter {
|
||||
/// Return whether this AppAdapter supports max-fps controls for its display.
|
||||
virtual auto SupportsMaxFPS() -> bool const;
|
||||
|
||||
/// Return whether audio should be silenced when the app goes inactive. On
|
||||
/// Desktop systems it is generally normal to continue to hear things even
|
||||
/// if their windows are hidden, but on mobile we probably want to silence
|
||||
/// our audio when phone calls, ads, etc. pop up over it. Note that this
|
||||
/// is called each time the app goes inactive, so the adapter may choose
|
||||
/// to selectively silence audio depending on what caused the inactive
|
||||
/// switch.
|
||||
virtual auto ShouldSilenceAudioForInactive() -> bool const;
|
||||
|
||||
/// Return whether this platform supports soft-quit. A soft quit is
|
||||
/// when the app is reset/backgrounded/etc. but remains running in case
|
||||
/// needed again. Generally this is the behavior on mobile apps.
|
||||
@ -206,22 +200,6 @@ class AppAdapter {
|
||||
virtual auto GetKeyRepeatDelay() -> float;
|
||||
virtual auto GetKeyRepeatInterval() -> float;
|
||||
|
||||
/// Return whether clipboard operations are supported at all. This gets
|
||||
/// called when determining whether to display clipboard related UI
|
||||
/// elements/etc.
|
||||
auto ClipboardIsSupported() -> bool;
|
||||
|
||||
/// Return whether there is currently text on the clipboard.
|
||||
auto ClipboardHasText() -> bool;
|
||||
|
||||
/// Set current clipboard text. Raises an Exception if clipboard is
|
||||
/// unsupported.
|
||||
void ClipboardSetText(const std::string& text);
|
||||
|
||||
/// Return current text from the clipboard. Raises an Exception if
|
||||
/// clipboard is unsupported or if there's no text on the clipboard.
|
||||
auto ClipboardGetText() -> std::string;
|
||||
|
||||
/// Push a raw pointer Runnable to the platform's 'main' thread. The main
|
||||
/// thread should call its RunAndLogErrors() method and then delete it.
|
||||
virtual void DoPushMainThreadRunnable(Runnable* runnable) = 0;
|
||||
@ -239,25 +217,19 @@ class AppAdapter {
|
||||
/// Asynchronously kick off a native review request.
|
||||
void NativeReviewRequest();
|
||||
|
||||
protected:
|
||||
virtual ~AppAdapter();
|
||||
|
||||
virtual auto DoClipboardIsSupported() -> bool;
|
||||
virtual auto DoClipboardHasText() -> bool;
|
||||
virtual void DoClipboardSetText(const std::string& text);
|
||||
virtual auto DoClipboardGetText() -> std::string;
|
||||
|
||||
protected:
|
||||
virtual ~AppAdapter();
|
||||
|
||||
/// Override to implement native review requests. Will be called in the
|
||||
/// main thread.
|
||||
virtual void DoNativeReviewRequest();
|
||||
|
||||
private:
|
||||
void OnAppSuspend_();
|
||||
void OnAppUnsuspend_();
|
||||
|
||||
bool app_suspended_{};
|
||||
bool have_clipboard_is_supported_{};
|
||||
bool clipboard_is_supported_{};
|
||||
};
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
@ -66,7 +66,6 @@ class AppAdapterApple : public AppAdapter {
|
||||
private:
|
||||
class ScopedAllowGraphics_;
|
||||
|
||||
// void UpdateScreenSizes_();
|
||||
void ReloadRenderer_(const GraphicsSettings* settings);
|
||||
|
||||
std::thread::id graphics_thread_{};
|
||||
|
||||
@ -471,6 +471,9 @@ void AppAdapterSDL::HandleSDLEvent_(const SDL_Event& event) {
|
||||
// it to the config so that UIs can poll for it and pick up the
|
||||
// change. We don't do this on other platforms where a maximized
|
||||
// window is more distinctly different than a fullscreen one.
|
||||
// Though I guess some Linux window managers have a fullscreen
|
||||
// function so theoretically we should there. Le sigh. Maybe SDL
|
||||
// 3 will tidy up this situation.
|
||||
fullscreen_ = true;
|
||||
g_base->logic->event_loop()->PushCall([] {
|
||||
g_base->python->objs()
|
||||
@ -497,18 +500,22 @@ void AppAdapterSDL::HandleSDLEvent_(const SDL_Event& event) {
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_HIDDEN: {
|
||||
// Let's keep track of when we're hidden so we can stop drawing
|
||||
// and sleep more. Theoretically we could put the app into a full
|
||||
// suspended state like we do on mobile (pausing event loops/etc.)
|
||||
// but that would be more involved; we'd need to ignore most SDL
|
||||
// events while sleeping (except for SDL_WINDOWEVENT_SHOWN) and
|
||||
// would need to rebuild our controller lists/etc when we resume.
|
||||
// For now just gonna keep things simple and keep running.
|
||||
// We plug this into the app's overall 'Active' state so it can
|
||||
// pause stuff or throttle down processing or whatever else.
|
||||
if (!hidden_) {
|
||||
g_base->SetAppActive(false);
|
||||
}
|
||||
// Also note that we are *completely* hidden, so we can totally
|
||||
// stop drawing ('Inactive' app state does not imply this in and
|
||||
// of itself).
|
||||
hidden_ = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_WINDOWEVENT_SHOWN: {
|
||||
if (hidden_) {
|
||||
g_base->SetAppActive(true);
|
||||
}
|
||||
hidden_ = false;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -14,8 +14,8 @@ void AppMode::OnActivate() {}
|
||||
void AppMode::OnDeactivate() {}
|
||||
|
||||
void AppMode::OnAppStart() {}
|
||||
void AppMode::OnAppPause() {}
|
||||
void AppMode::OnAppResume() {}
|
||||
void AppMode::OnAppSuspend() {}
|
||||
void AppMode::OnAppUnsuspend() {}
|
||||
void AppMode::OnAppShutdown() {}
|
||||
void AppMode::OnAppShutdownComplete() {}
|
||||
|
||||
|
||||
@ -26,8 +26,8 @@ class AppMode {
|
||||
|
||||
/// Logic thread callbacks that run while the app-mode is active.
|
||||
virtual void OnAppStart();
|
||||
virtual void OnAppPause();
|
||||
virtual void OnAppResume();
|
||||
virtual void OnAppSuspend();
|
||||
virtual void OnAppUnsuspend();
|
||||
virtual void OnAppShutdown();
|
||||
virtual void OnAppShutdownComplete();
|
||||
virtual void DoApplyAppConfig();
|
||||
|
||||
@ -17,7 +17,8 @@
|
||||
#include <alc.h>
|
||||
#endif
|
||||
|
||||
#if BA_OSTYPE_ANDROID
|
||||
#if BA_OPENAL_IS_SOFT
|
||||
#define AL_ALEXT_PROTOTYPES
|
||||
#include <alext.h>
|
||||
#endif
|
||||
|
||||
|
||||
@ -33,9 +33,9 @@ void Audio::Reset() {
|
||||
|
||||
void Audio::OnAppStart() { assert(g_base->InLogicThread()); }
|
||||
|
||||
void Audio::OnAppPause() { assert(g_base->InLogicThread()); }
|
||||
void Audio::OnAppSuspend() { assert(g_base->InLogicThread()); }
|
||||
|
||||
void Audio::OnAppResume() { assert(g_base->InLogicThread()); }
|
||||
void Audio::OnAppUnsuspend() { assert(g_base->InLogicThread()); }
|
||||
|
||||
void Audio::OnAppShutdown() { assert(g_base->InLogicThread()); }
|
||||
|
||||
|
||||
@ -21,8 +21,8 @@ class Audio {
|
||||
void Reset();
|
||||
|
||||
virtual void OnAppStart();
|
||||
virtual void OnAppPause();
|
||||
virtual void OnAppResume();
|
||||
virtual void OnAppSuspend();
|
||||
virtual void OnAppUnsuspend();
|
||||
virtual void OnAppShutdown();
|
||||
virtual void OnAppShutdownComplete();
|
||||
virtual void DoApplyAppConfig();
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "ballistica/base/app_adapter/app_adapter.h"
|
||||
#include "ballistica/base/assets/assets.h"
|
||||
#include "ballistica/base/assets/sound_asset.h"
|
||||
#include "ballistica/base/audio/al_sys.h"
|
||||
@ -27,9 +28,12 @@ namespace ballistica::base {
|
||||
extern std::string g_rift_audio_device_name;
|
||||
#endif
|
||||
|
||||
#if BA_OSTYPE_ANDROID
|
||||
#if BA_OPENAL_IS_SOFT
|
||||
LPALCDEVICEPAUSESOFT alcDevicePauseSOFT{};
|
||||
LPALCDEVICERESUMESOFT alcDeviceResumeSOFT{};
|
||||
LPALCRESETDEVICESOFT alcResetDeviceSOFT{};
|
||||
LPALEVENTCALLBACKSOFT alEventCallbackSOFT{};
|
||||
LPALEVENTCONTROLSOFT alEventControlSOFT{};
|
||||
#endif
|
||||
|
||||
const int kAudioProcessIntervalNormal{500 * 1000};
|
||||
@ -107,29 +111,24 @@ class AudioServer::ThreadSource_ : public Object {
|
||||
}
|
||||
|
||||
private:
|
||||
bool looping_{};
|
||||
std::unique_ptr<AudioSource> client_source_;
|
||||
float fade_{1.0f};
|
||||
float gain_{1.0f};
|
||||
AudioServer* audio_thread_{};
|
||||
bool valid_{};
|
||||
const Object::Ref<SoundAsset>* source_sound_{};
|
||||
int id_{};
|
||||
uint32_t play_count_{};
|
||||
bool looping_{};
|
||||
bool valid_{};
|
||||
bool is_actually_playing_{};
|
||||
bool want_to_play_{};
|
||||
#if BA_ENABLE_AUDIO
|
||||
ALuint source_{};
|
||||
#endif
|
||||
bool is_streamed_{};
|
||||
|
||||
/// Whether we should be designated as "music" next time we play.
|
||||
bool is_music_{};
|
||||
|
||||
/// Whether currently playing as music.
|
||||
bool current_is_music_{};
|
||||
|
||||
uint32_t play_count_{};
|
||||
float fade_{1.0f};
|
||||
float gain_{1.0f};
|
||||
std::unique_ptr<AudioSource> client_source_;
|
||||
AudioServer* audio_server_{};
|
||||
const Object::Ref<SoundAsset>* source_sound_{};
|
||||
#if BA_ENABLE_AUDIO
|
||||
ALuint source_{};
|
||||
Object::Ref<AudioStreamer> streamer_;
|
||||
#endif
|
||||
}; // ThreadSource
|
||||
@ -155,6 +154,22 @@ void AudioServer::OnMainThreadStartApp() {
|
||||
event_loop_->PushCallSynchronous([this] { OnAppStartInThread_(); });
|
||||
}
|
||||
|
||||
#if BA_OPENAL_IS_SOFT
|
||||
static void ALEventCallback_(ALenum eventType, ALuint object, ALuint param,
|
||||
ALsizei length, const ALchar* message,
|
||||
ALvoid* userParam) noexcept {
|
||||
if (eventType == AL_EVENT_TYPE_DISCONNECTED_SOFT) {
|
||||
if (g_base->audio_server) {
|
||||
g_base->audio_server->event_loop()->PushCall(
|
||||
[] { g_base->audio_server->OnDeviceDisconnected(); });
|
||||
}
|
||||
} else {
|
||||
Log(LogLevel::kWarning, "Got unexpected OpenAL callback event "
|
||||
+ std::to_string(static_cast<int>(eventType)));
|
||||
}
|
||||
}
|
||||
#endif // BA_OPENAL_IS_SOFT
|
||||
|
||||
void AudioServer::OnAppStartInThread_() {
|
||||
assert(g_base->InAudioThread());
|
||||
|
||||
@ -167,7 +182,7 @@ void AudioServer::OnAppStartInThread_() {
|
||||
|
||||
// Bring up OpenAL stuff.
|
||||
{
|
||||
const char* al_device_name = nullptr;
|
||||
const char* al_device_name{};
|
||||
|
||||
// On the rift build in vr mode we need to make sure we open the rift audio
|
||||
// device.
|
||||
@ -211,21 +226,42 @@ void AudioServer::OnAppStartInThread_() {
|
||||
"connected?");
|
||||
}
|
||||
impl_->alc_context = alcCreateContext(device, nullptr);
|
||||
BA_PRECONDITION(impl_->alc_context);
|
||||
BA_PRECONDITION(alcMakeContextCurrent(impl_->alc_context));
|
||||
if (!impl_->alc_context) {
|
||||
FatalError(
|
||||
"Unable to init audio. Do you have speakers/headphones/etc. "
|
||||
"connected?");
|
||||
}
|
||||
BA_PRECONDITION_FATAL(impl_->alc_context);
|
||||
BA_PRECONDITION_FATAL(alcMakeContextCurrent(impl_->alc_context));
|
||||
CHECK_AL_ERROR;
|
||||
|
||||
#if BA_OSTYPE_ANDROID
|
||||
if (alcIsExtensionPresent(device, "ALC_SOFT_pause_device")) {
|
||||
alcDevicePauseSOFT = reinterpret_cast<LPALCDEVICEPAUSESOFT>(
|
||||
alcGetProcAddress(device, "alcDevicePauseSOFT"));
|
||||
BA_PRECONDITION_FATAL(alcDevicePauseSOFT != nullptr);
|
||||
alcDeviceResumeSOFT = reinterpret_cast<LPALCDEVICERESUMESOFT>(
|
||||
alcGetProcAddress(device, "alcDeviceResumeSOFT"));
|
||||
BA_PRECONDITION_FATAL(alcDeviceResumeSOFT != nullptr);
|
||||
} else {
|
||||
FatalError("ALC_SOFT pause/resume functionality not found.");
|
||||
}
|
||||
#if BA_OPENAL_IS_SOFT
|
||||
// Currently assuming the pause/resume and reset extensions are present.
|
||||
// if (alcIsExtensionPresent(device, "ALC_SOFT_pause_device")) {
|
||||
alcDevicePauseSOFT = reinterpret_cast<LPALCDEVICEPAUSESOFT>(
|
||||
alcGetProcAddress(device, "alcDevicePauseSOFT"));
|
||||
BA_PRECONDITION_FATAL(alcDevicePauseSOFT != nullptr);
|
||||
alcDeviceResumeSOFT = reinterpret_cast<LPALCDEVICERESUMESOFT>(
|
||||
alcGetProcAddress(device, "alcDeviceResumeSOFT"));
|
||||
BA_PRECONDITION_FATAL(alcDeviceResumeSOFT != nullptr);
|
||||
alcResetDeviceSOFT = reinterpret_cast<LPALCRESETDEVICESOFT>(
|
||||
alcGetProcAddress(device, "alcResetDeviceSOFT"));
|
||||
BA_PRECONDITION_FATAL(alcResetDeviceSOFT != nullptr);
|
||||
alEventCallbackSOFT = reinterpret_cast<LPALEVENTCALLBACKSOFT>(
|
||||
alcGetProcAddress(device, "alEventCallbackSOFT"));
|
||||
BA_PRECONDITION_FATAL(alEventCallbackSOFT != nullptr);
|
||||
alEventControlSOFT = reinterpret_cast<LPALEVENTCONTROLSOFT>(
|
||||
alcGetProcAddress(device, "alEventControlSOFT"));
|
||||
BA_PRECONDITION_FATAL(alEventControlSOFT != nullptr);
|
||||
|
||||
// Ask to be notified when a device is disconnected.
|
||||
alEventCallbackSOFT(ALEventCallback_, nullptr);
|
||||
CHECK_AL_ERROR;
|
||||
ALenum types[] = {AL_EVENT_TYPE_DISCONNECTED_SOFT};
|
||||
alEventControlSOFT(1, types, AL_TRUE);
|
||||
// } else {
|
||||
// FatalError("ALC_SOFT pause/resume functionality not found.");
|
||||
// }
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -259,6 +295,7 @@ void AudioServer::OnAppStartInThread_() {
|
||||
// Now make available any stopped sources (should be all of them).
|
||||
UpdateAvailableSources_();
|
||||
|
||||
last_started_playing_time_ = g_core->GetAppTimeSeconds();
|
||||
#endif // BA_ENABLE_AUDIO
|
||||
}
|
||||
|
||||
@ -334,23 +371,27 @@ void AudioServer::SetSuspended_(bool suspend) {
|
||||
#endif
|
||||
|
||||
// Pause OpenALSoft.
|
||||
#if BA_OSTYPE_ANDROID
|
||||
#if BA_OPENAL_IS_SOFT
|
||||
BA_PRECONDITION_FATAL(alcDevicePauseSOFT != nullptr);
|
||||
BA_PRECONDITION_FATAL(impl_ != nullptr && impl_->alc_context != nullptr);
|
||||
auto* device = alcGetContextsDevice(impl_->alc_context);
|
||||
BA_PRECONDITION_FATAL(device != nullptr);
|
||||
|
||||
try {
|
||||
g_core->platform->LowLevelDebugLog(
|
||||
"Calling alcDevicePauseSOFT at "
|
||||
+ std::to_string(g_core->GetAppTimeSeconds()));
|
||||
alcDevicePauseSOFT(device);
|
||||
} catch (const std::exception& e) {
|
||||
g_core->platform->DebugLog(
|
||||
std::string("EXC pausing alcDevice: ")
|
||||
+ g_core->platform->DemangleCXXSymbol(typeid(e).name()) + " "
|
||||
+ e.what());
|
||||
throw;
|
||||
Log(LogLevel::kError,
|
||||
"Error in alcDevicePauseSOFT at time "
|
||||
+ std::to_string(g_core->GetAppTimeSeconds())
|
||||
+ "( playing since "
|
||||
+ std::to_string(last_started_playing_time_)
|
||||
+ "): " + g_core->platform->DemangleCXXSymbol(typeid(e).name())
|
||||
+ " " + e.what());
|
||||
} catch (...) {
|
||||
g_core->platform->DebugLog("UNKNOWN EXC pausing alcDevice");
|
||||
throw;
|
||||
Log(LogLevel::kError, "Unknown error in alcDevicePauseSOFT");
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -372,25 +413,28 @@ void AudioServer::SetSuspended_(bool suspend) {
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// On android lets tell openal-soft to stop processing.
|
||||
#if BA_OSTYPE_ANDROID
|
||||
// With OpenALSoft lets tell openal-soft to resume processing.
|
||||
#if BA_OPENAL_IS_SOFT
|
||||
BA_PRECONDITION_FATAL(alcDeviceResumeSOFT != nullptr);
|
||||
BA_PRECONDITION_FATAL(impl_ != nullptr && impl_->alc_context != nullptr);
|
||||
auto* device = alcGetContextsDevice(impl_->alc_context);
|
||||
BA_PRECONDITION_FATAL(device != nullptr);
|
||||
try {
|
||||
g_core->platform->LowLevelDebugLog(
|
||||
"Calling alcDeviceResumeSOFT at "
|
||||
+ std::to_string(g_core->GetAppTimeSeconds()));
|
||||
alcDeviceResumeSOFT(device);
|
||||
} catch (const std::exception& e) {
|
||||
g_core->platform->DebugLog(
|
||||
std::string("EXC resuming alcDevice: ")
|
||||
+ g_core->platform->DemangleCXXSymbol(typeid(e).name()) + " "
|
||||
+ e.what());
|
||||
throw;
|
||||
Log(LogLevel::kError,
|
||||
"Error in alcDeviceResumeSOFT at time "
|
||||
+ std::to_string(g_core->GetAppTimeSeconds()) + ": "
|
||||
+ g_core->platform->DemangleCXXSymbol(typeid(e).name()) + " "
|
||||
+ e.what());
|
||||
} catch (...) {
|
||||
g_core->platform->DebugLog("UNKNOWN EXC resuming alcDevice");
|
||||
throw;
|
||||
Log(LogLevel::kError, "Unknown error in alcDeviceResumeSOFT");
|
||||
}
|
||||
#endif
|
||||
last_started_playing_time_ = g_core->GetAppTimeSeconds();
|
||||
suspended_ = false;
|
||||
#if BA_ENABLE_AUDIO
|
||||
CHECK_AL_ERROR;
|
||||
@ -477,8 +521,8 @@ void AudioServer::PushSourcePlayCall(uint32_t play_id,
|
||||
|
||||
// Let's take this opportunity to pass on newly available sources.
|
||||
// This way the more things clients are playing, the more
|
||||
// tight our source availability checking gets (instead of solely relying on
|
||||
// our periodic process() calls).
|
||||
// tight our source availability checking gets (instead of solely relying
|
||||
// on our periodic process() calls).
|
||||
UpdateAvailableSources_();
|
||||
});
|
||||
}
|
||||
@ -685,12 +729,58 @@ void AudioServer::UpdateMusicPlayState_() {
|
||||
}
|
||||
}
|
||||
|
||||
void AudioServer::ProcessDeviceDisconnects_(seconds_t real_time_seconds) {
|
||||
#if BA_OPENAL_IS_SOFT
|
||||
// If our device has been disconnected, try to reconnect it
|
||||
// periodically.
|
||||
auto* device = alcGetContextsDevice(impl_->alc_context);
|
||||
BA_PRECONDITION_FATAL(device != nullptr);
|
||||
ALCint connected{-1};
|
||||
alcGetIntegerv(device, ALC_CONNECTED, sizeof(connected), &connected);
|
||||
CHECK_AL_ERROR;
|
||||
if (connected == 0 && real_time_seconds - last_reset_attempt_time_ > 10.0) {
|
||||
Log(LogLevel::kInfo, "OpenAL device disconnected; resetting...");
|
||||
last_reset_attempt_time_ = real_time_seconds;
|
||||
BA_PRECONDITION_FATAL(alcResetDeviceSOFT != nullptr);
|
||||
alcResetDeviceSOFT(device, nullptr);
|
||||
CHECK_AL_ERROR;
|
||||
|
||||
// Make noise if this ever fails to bring the device back.
|
||||
ALCint connected{-1};
|
||||
alcGetIntegerv(device, ALC_CONNECTED, sizeof(connected), &connected);
|
||||
CHECK_AL_ERROR;
|
||||
|
||||
// If we were successful, don't require a wait for the next reset.
|
||||
// (otherwise plugging in headphones and then unplugging will stay quiet
|
||||
// for 10 seconds).
|
||||
if (connected == 1) {
|
||||
last_reset_attempt_time_ = -999.0;
|
||||
}
|
||||
|
||||
if (connected == 0 && !reported_reset_fail_) {
|
||||
reported_reset_fail_ = true;
|
||||
Log(LogLevel::kError, "alcResetDeviceSOFT failed to reconnect device.");
|
||||
}
|
||||
}
|
||||
#endif // BA_OPENAL_IS_SOFT
|
||||
}
|
||||
|
||||
void AudioServer::OnDeviceDisconnected() {
|
||||
assert(g_base->InAudioThread());
|
||||
// All we do here is run an explicit Process_. This only saves us a half
|
||||
// second or so over letting the timer do it, but hey we'll take it.
|
||||
Process_();
|
||||
}
|
||||
|
||||
void AudioServer::Process_() {
|
||||
assert(g_base->InAudioThread());
|
||||
millisecs_t real_time = g_core->GetAppTimeMillisecs();
|
||||
seconds_t real_time_seconds = g_core->GetAppTimeSeconds();
|
||||
millisecs_t real_time_millisecs = real_time_seconds * 1000;
|
||||
|
||||
// If we're suspended we don't do nothin'.
|
||||
// Only do real work if we're in normal running mode.
|
||||
if (!suspended_ && !shutting_down_) {
|
||||
ProcessDeviceDisconnects_(real_time_seconds);
|
||||
|
||||
// Do some loading...
|
||||
have_pending_loads_ = g_base->assets->RunPendingAudioLoads();
|
||||
|
||||
@ -698,19 +788,33 @@ void AudioServer::Process_() {
|
||||
UpdateAvailableSources_();
|
||||
|
||||
// Update our fading sound volumes.
|
||||
if (real_time - last_sound_fade_process_time_ > 50) {
|
||||
if (real_time_millisecs - last_sound_fade_process_time_ > 50) {
|
||||
ProcessSoundFades_();
|
||||
last_sound_fade_process_time_ = real_time;
|
||||
last_sound_fade_process_time_ = real_time_millisecs;
|
||||
}
|
||||
|
||||
// Update streaming sources.
|
||||
if (real_time - last_stream_process_time_ > 100) {
|
||||
last_stream_process_time_ = real_time;
|
||||
if (real_time_millisecs - last_stream_process_time_ > 100) {
|
||||
last_stream_process_time_ = real_time_millisecs;
|
||||
for (auto&& i : streaming_sources_) {
|
||||
i->Update();
|
||||
}
|
||||
}
|
||||
|
||||
// If the app has switched active/inactive state, update our volumes (we
|
||||
// may silence our audio in these cases).
|
||||
auto app_active = g_base->app_active();
|
||||
if (app_active != app_active_) {
|
||||
app_active_ = app_active;
|
||||
app_active_volume_ =
|
||||
(!app_active && g_base->app_adapter->ShouldSilenceAudioForInactive())
|
||||
? 0.0f
|
||||
: 1.0f;
|
||||
for (auto&& i : sources_) {
|
||||
i->UpdateVolume();
|
||||
}
|
||||
}
|
||||
|
||||
#if BA_ENABLE_AUDIO
|
||||
CHECK_AL_ERROR;
|
||||
#endif
|
||||
@ -781,7 +885,8 @@ void AudioServer::ProcessSoundFades_() {
|
||||
}
|
||||
|
||||
void AudioServer::FadeSoundOut(uint32_t play_id, uint32_t time) {
|
||||
// Pop a new node on the list (this won't overwrite the old if there is one).
|
||||
// Pop a new node on the list (this won't overwrite the old if there is
|
||||
// one).
|
||||
sound_fade_nodes_.insert(
|
||||
std::make_pair(play_id, SoundFadeNode_(play_id, time, true)));
|
||||
}
|
||||
@ -792,9 +897,9 @@ void AudioServer::FadeSoundOut(uint32_t play_id, uint32_t time) {
|
||||
// delete c;
|
||||
// }
|
||||
|
||||
AudioServer::ThreadSource_::ThreadSource_(AudioServer* audio_thread_in,
|
||||
AudioServer::ThreadSource_::ThreadSource_(AudioServer* audio_server_in,
|
||||
int id_in, bool* valid_out)
|
||||
: id_(id_in), audio_thread_(audio_thread_in) {
|
||||
: id_(id_in), audio_server_(audio_server_in) {
|
||||
#if BA_ENABLE_AUDIO
|
||||
assert(g_core);
|
||||
assert(valid_out != nullptr);
|
||||
@ -839,10 +944,10 @@ AudioServer::ThreadSource_::~ThreadSource_() {
|
||||
Stop();
|
||||
|
||||
// Remove us from sources list.
|
||||
for (auto i = audio_thread_->sources_.begin();
|
||||
i != audio_thread_->sources_.end(); ++i) {
|
||||
for (auto i = audio_server_->sources_.begin();
|
||||
i != audio_server_->sources_.end(); ++i) {
|
||||
if (*i == this) {
|
||||
audio_thread_->sources_.erase(i);
|
||||
audio_server_->sources_.erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -866,8 +971,8 @@ void AudioServer::ThreadSource_::UpdateAvailability() {
|
||||
|
||||
assert(g_base->InAudioThread());
|
||||
|
||||
// If it's waiting to be picked up by a client or has pending client commands,
|
||||
// skip.
|
||||
// If it's waiting to be picked up by a client or has pending client
|
||||
// commands, skip.
|
||||
if (!client_source_->TryLock(6)) {
|
||||
return;
|
||||
}
|
||||
@ -879,10 +984,9 @@ void AudioServer::ThreadSource_::UpdateAvailability() {
|
||||
}
|
||||
|
||||
// We consider ourselves busy if there's an active looping play command
|
||||
// (regardless of its actual physical play state - music could be turned off,
|
||||
// stuttering, etc.).
|
||||
// If it's non-looping, we check its play state and snatch it if it's not
|
||||
// playing.
|
||||
// (regardless of its actual physical play state - music could be turned
|
||||
// off, stuttering, etc.). If it's non-looping, we check its play state and
|
||||
// snatch it if it's not playing.
|
||||
bool busy;
|
||||
if (looping_ || (is_streamed_ && streamer_.Exists() && streamer_->loops())) {
|
||||
busy = want_to_play_;
|
||||
@ -1079,12 +1183,12 @@ void AudioServer::ThreadSource_::ExecPlay() {
|
||||
looping_ = false;
|
||||
|
||||
// Push us on the list of streaming sources if we're not on it.
|
||||
for (auto&& i : audio_thread_->streaming_sources_) {
|
||||
for (auto&& i : audio_server_->streaming_sources_) {
|
||||
if (i == this) {
|
||||
throw Exception();
|
||||
}
|
||||
}
|
||||
audio_thread_->streaming_sources_.push_back(this);
|
||||
audio_server_->streaming_sources_.push_back(this);
|
||||
|
||||
// Make sure stereo sounds aren't positional.
|
||||
// This is default behavior on Mac/Win, but we enforce it for linux.
|
||||
@ -1162,10 +1266,10 @@ void AudioServer::ThreadSource_::ExecStop() {
|
||||
if (streamer_.Exists()) {
|
||||
assert(is_streamed_);
|
||||
streamer_->Stop();
|
||||
for (auto i = audio_thread_->streaming_sources_.begin();
|
||||
i != audio_thread_->streaming_sources_.end(); ++i) {
|
||||
for (auto i = audio_server_->streaming_sources_.begin();
|
||||
i != audio_server_->streaming_sources_.end(); ++i) {
|
||||
if (*i == this) {
|
||||
audio_thread_->streaming_sources_.erase(i);
|
||||
audio_server_->streaming_sources_.erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1182,15 +1286,16 @@ void AudioServer::ThreadSource_::ExecStop() {
|
||||
void AudioServer::ThreadSource_::UpdateVolume() {
|
||||
#if BA_ENABLE_AUDIO
|
||||
assert(g_base->InAudioThread());
|
||||
if (g_base->audio_server->suspended_
|
||||
|| g_base->audio_server->shutting_down_) {
|
||||
if (audio_server_->suspended_ || audio_server_->shutting_down_) {
|
||||
return;
|
||||
}
|
||||
float val = gain_ * fade_;
|
||||
val *= audio_server_->app_active_volume_;
|
||||
|
||||
if (current_is_music()) {
|
||||
val *= audio_thread_->music_volume_ / 7.0f;
|
||||
val *= audio_server_->music_volume_ / 7.0f;
|
||||
} else {
|
||||
val *= audio_thread_->sound_volume_;
|
||||
val *= audio_server_->sound_volume_;
|
||||
}
|
||||
alSourcef(source_, AL_GAIN, std::max(0.0f, val));
|
||||
CHECK_AL_ERROR;
|
||||
@ -1208,7 +1313,7 @@ void AudioServer::ThreadSource_::UpdatePitch() {
|
||||
float val = 1.0f;
|
||||
if (current_is_music()) {
|
||||
} else {
|
||||
val *= audio_thread_->sound_pitch_;
|
||||
val *= audio_server_->sound_pitch_;
|
||||
}
|
||||
alSourcef(source_, AL_PITCH, val);
|
||||
CHECK_AL_ERROR;
|
||||
@ -1227,16 +1332,6 @@ void AudioServer::PushSetSoundPitchCall(float val) {
|
||||
event_loop()->PushCall([this, val] { SetSoundPitch_(val); });
|
||||
}
|
||||
|
||||
// void AudioServer::PushSetSuspendedCall(bool suspend) {
|
||||
// event_loop()->PushCall([this, suspend] {
|
||||
// if (g_buildconfig.ostype_android()) {
|
||||
// Log(LogLevel::kError, "Shouldn't be getting SetSuspendedCall on
|
||||
// android.");
|
||||
// }
|
||||
// SetSuspended_(suspend);
|
||||
// });
|
||||
// }
|
||||
|
||||
void AudioServer::PushComponentUnloadCall(
|
||||
const std::vector<Object::Ref<Asset>*>& components) {
|
||||
event_loop()->PushCall([components] {
|
||||
|
||||
@ -67,6 +67,8 @@ class AudioServer {
|
||||
|
||||
auto event_loop() const -> EventLoop* { return event_loop_; }
|
||||
|
||||
void OnDeviceDisconnected();
|
||||
|
||||
private:
|
||||
class ThreadSource_;
|
||||
struct Impl_;
|
||||
@ -90,6 +92,7 @@ class AudioServer {
|
||||
|
||||
void Reset_();
|
||||
void Process_();
|
||||
void ProcessDeviceDisconnects_(seconds_t real_time_seconds);
|
||||
|
||||
/// Send a component to the audio thread to delete.
|
||||
// void DeleteAssetComponent_(Asset* c);
|
||||
@ -115,12 +118,18 @@ class AudioServer {
|
||||
float sound_volume_{1.0f};
|
||||
float sound_pitch_{1.0f};
|
||||
float music_volume_{1.0f};
|
||||
float app_active_volume_{1.0f};
|
||||
|
||||
bool have_pending_loads_{};
|
||||
bool app_active_{true};
|
||||
bool suspended_{};
|
||||
bool shutdown_completed_{};
|
||||
bool shutting_down_{};
|
||||
bool reported_reset_fail_{};
|
||||
int al_source_count_{};
|
||||
seconds_t last_reset_attempt_time_{-999.0};
|
||||
seconds_t shutdown_start_time_{};
|
||||
seconds_t last_started_playing_time_{};
|
||||
millisecs_t last_sound_fade_process_time_{};
|
||||
|
||||
/// Indexed list of sources.
|
||||
@ -144,8 +153,6 @@ class AudioServer {
|
||||
|
||||
// Our list of sound media components to delete via the main thread.
|
||||
std::vector<const Object::Ref<SoundAsset>*> sound_ref_delete_list_;
|
||||
|
||||
int al_source_count_{};
|
||||
};
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
@ -195,9 +195,17 @@ void BaseFeatureSet::OnAssetsAvailable() {
|
||||
}
|
||||
|
||||
void BaseFeatureSet::StartApp() {
|
||||
// {
|
||||
// // TEST - recreate the ID python dumps in its thread tracebacks.
|
||||
// auto val = PyThread_get_thread_ident();
|
||||
// printf("MAIN THREAD IS %#018lx\n", val);
|
||||
// }
|
||||
|
||||
BA_PRECONDITION(g_core->InMainThread());
|
||||
BA_PRECONDITION(g_base);
|
||||
|
||||
auto start_time = g_core->GetAppTimeSeconds();
|
||||
|
||||
// Currently limiting this to once per process.
|
||||
BA_PRECONDITION(!called_start_app_);
|
||||
called_start_app_ = true;
|
||||
@ -248,6 +256,177 @@ void BaseFeatureSet::StartApp() {
|
||||
}
|
||||
|
||||
g_core->LifecycleLog("start-app end (main thread)");
|
||||
|
||||
// Make some noise if this takes more than a few seconds. If we pass 5
|
||||
// seconds or so we start to trigger App-Not-Responding reports which
|
||||
// isn't good.
|
||||
auto duration = g_core->GetAppTimeSeconds() - start_time;
|
||||
if (duration > 3.0) {
|
||||
char buffer[128];
|
||||
snprintf(buffer, sizeof(buffer),
|
||||
"StartApp() took too long (%.2lf seconds).", duration);
|
||||
Log(LogLevel::kWarning, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void BaseFeatureSet::SuspendApp() {
|
||||
assert(g_core);
|
||||
assert(g_core->InMainThread());
|
||||
|
||||
if (app_suspended_) {
|
||||
Log(LogLevel::kWarning,
|
||||
"AppAdapter::SuspendApp() called with app already suspended.");
|
||||
return;
|
||||
}
|
||||
|
||||
millisecs_t start_time{core::CorePlatform::GetCurrentMillisecs()};
|
||||
|
||||
// Apple mentioned 5 seconds to run stuff once backgrounded or they bring
|
||||
// down the hammer. Let's aim to stay under 2.
|
||||
millisecs_t max_duration{2000};
|
||||
|
||||
g_core->platform->LowLevelDebugLog(
|
||||
"SuspendApp@"
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs()));
|
||||
app_suspended_ = true;
|
||||
|
||||
// IMPORTANT: Any pause related stuff that event-loop-threads need to do
|
||||
// should be done from their registered pause-callbacks. If we instead
|
||||
// push runnables to them from here they may or may not be called before
|
||||
// their event-loop is actually paused.
|
||||
|
||||
// Pause all event loops.
|
||||
EventLoop::SetEventLoopsSuspended(true);
|
||||
|
||||
if (g_base->network_reader) {
|
||||
g_base->network_reader->OnAppSuspend();
|
||||
}
|
||||
g_base->networking->OnAppSuspend();
|
||||
|
||||
// We assume that the OS will completely suspend our process the moment we
|
||||
// return from this call (though this is not technically true on all
|
||||
// platforms). So we want to spin here and give our various event loop
|
||||
// threads time to park themselves.
|
||||
std::vector<EventLoop*> running_loops;
|
||||
do {
|
||||
// If/when we get to a point with no threads waiting to be paused, we're
|
||||
// good to go.
|
||||
// auto loops{EventLoop::GetStillSuspendingEventLoops()};
|
||||
running_loops = EventLoop::GetStillSuspendingEventLoops();
|
||||
// running_loop_count = loops.size();
|
||||
if (running_loops.empty()) {
|
||||
if (g_buildconfig.debug_build()) {
|
||||
Log(LogLevel::kDebug,
|
||||
"SuspendApp() completed in "
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs()
|
||||
- start_time)
|
||||
+ "ms.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
} while (std::abs(core::CorePlatform::GetCurrentMillisecs() - start_time)
|
||||
< max_duration);
|
||||
|
||||
// If we made it here, we timed out. Complain.
|
||||
std::string msg =
|
||||
std::string("SuspendApp() took too long; ")
|
||||
+ std::to_string(running_loops.size())
|
||||
+ " event-loops not yet suspended after "
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs() - start_time)
|
||||
+ " ms: (";
|
||||
bool first = true;
|
||||
for (auto* loop : running_loops) {
|
||||
if (!first) {
|
||||
msg += ", ";
|
||||
}
|
||||
// Note: not adding a default here so compiler complains if we
|
||||
// add/change something.
|
||||
switch (loop->identifier()) {
|
||||
case EventLoopID::kInvalid:
|
||||
msg += "invalid";
|
||||
break;
|
||||
case EventLoopID::kLogic:
|
||||
msg += "logic";
|
||||
break;
|
||||
case EventLoopID::kAssets:
|
||||
msg += "assets";
|
||||
break;
|
||||
case EventLoopID::kFileOut:
|
||||
msg += "fileout";
|
||||
break;
|
||||
case EventLoopID::kMain:
|
||||
msg += "main";
|
||||
break;
|
||||
case EventLoopID::kAudio:
|
||||
msg += "audio";
|
||||
break;
|
||||
case EventLoopID::kNetworkWrite:
|
||||
msg += "networkwrite";
|
||||
break;
|
||||
case EventLoopID::kSuicide:
|
||||
msg += "suicide";
|
||||
break;
|
||||
case EventLoopID::kStdin:
|
||||
msg += "stdin";
|
||||
break;
|
||||
case EventLoopID::kBGDynamics:
|
||||
msg += "bgdynamics";
|
||||
break;
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
msg += ").";
|
||||
|
||||
Log(LogLevel::kError, msg);
|
||||
}
|
||||
|
||||
void BaseFeatureSet::UnsuspendApp() {
|
||||
assert(g_core);
|
||||
assert(g_core->InMainThread());
|
||||
|
||||
if (!app_suspended_) {
|
||||
Log(LogLevel::kWarning,
|
||||
"AppAdapter::UnsuspendApp() called with app not in suspendedstate.");
|
||||
return;
|
||||
}
|
||||
millisecs_t start_time{core::CorePlatform::GetCurrentMillisecs()};
|
||||
g_core->platform->LowLevelDebugLog(
|
||||
"UnsuspendApp@"
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs()));
|
||||
app_suspended_ = false;
|
||||
|
||||
// Spin all event-loops back up.
|
||||
EventLoop::SetEventLoopsSuspended(false);
|
||||
|
||||
// Run resumes that expect to happen in the main thread.
|
||||
g_base->network_reader->OnAppUnsuspend();
|
||||
g_base->networking->OnAppUnsuspend();
|
||||
|
||||
// When resuming from a suspended state, we may want to pause whatever
|
||||
// game was running when we last were active.
|
||||
//
|
||||
// TODO(efro): we should make this smarter so it doesn't happen if we're
|
||||
// in a network game or something that we can't pause; bringing up the
|
||||
// menu doesn't really accomplish anything there.
|
||||
//
|
||||
// In general this probably should be handled at a higher level.
|
||||
// if (g_core->should_pause_active_game) {
|
||||
// g_core->should_pause_active_game = false;
|
||||
|
||||
// // If we've been completely backgrounded, send a menu-press command to
|
||||
// // the game; this will bring up a pause menu if we're in the game/etc.
|
||||
// if (!g_base->ui->MainMenuVisible()) {
|
||||
// g_base->ui->PushMainMenuPressCall(nullptr);
|
||||
// }
|
||||
// }
|
||||
|
||||
if (g_buildconfig.debug_build()) {
|
||||
Log(LogLevel::kDebug,
|
||||
"UnsuspendApp() completed in "
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs()
|
||||
- start_time)
|
||||
+ "ms.");
|
||||
}
|
||||
}
|
||||
|
||||
void BaseFeatureSet::OnAppShutdownComplete() {
|
||||
@ -730,8 +909,6 @@ void BaseFeatureSet::ShutdownSuppressDisallow() {
|
||||
shutdown_suppress_disallowed_ = true;
|
||||
}
|
||||
|
||||
// auto BaseFeatureSet::GetReturnValue() const -> int { return return_value(); }
|
||||
|
||||
void BaseFeatureSet::QuitApp(bool confirm, QuitType quit_type) {
|
||||
// If they want a confirm dialog and we're able to present one, do that.
|
||||
if (confirm && !g_core->HeadlessMode() && !g_base->input->IsInputLocked()
|
||||
@ -760,4 +937,57 @@ void BaseFeatureSet::PushMainThreadRunnable(Runnable* runnable) {
|
||||
app_adapter->DoPushMainThreadRunnable(runnable);
|
||||
}
|
||||
|
||||
auto BaseFeatureSet::ClipboardIsSupported() -> bool {
|
||||
// We only call our actual virtual function once.
|
||||
if (!have_clipboard_is_supported_) {
|
||||
clipboard_is_supported_ = app_adapter->DoClipboardIsSupported();
|
||||
have_clipboard_is_supported_ = true;
|
||||
}
|
||||
return clipboard_is_supported_;
|
||||
}
|
||||
|
||||
auto BaseFeatureSet::ClipboardHasText() -> bool {
|
||||
// If subplatform says they don't support clipboards, don't even ask.
|
||||
if (!ClipboardIsSupported()) {
|
||||
return false;
|
||||
}
|
||||
return app_adapter->DoClipboardHasText();
|
||||
}
|
||||
|
||||
void BaseFeatureSet::ClipboardSetText(const std::string& text) {
|
||||
// If subplatform says they don't support clipboards, this is an error.
|
||||
if (!ClipboardIsSupported()) {
|
||||
throw Exception("ClipboardSetText called with no clipboard support.",
|
||||
PyExcType::kRuntime);
|
||||
}
|
||||
app_adapter->DoClipboardSetText(text);
|
||||
}
|
||||
|
||||
auto BaseFeatureSet::ClipboardGetText() -> std::string {
|
||||
// If subplatform says they don't support clipboards, this is an error.
|
||||
if (!ClipboardIsSupported()) {
|
||||
throw Exception("ClipboardGetText called with no clipboard support.",
|
||||
PyExcType::kRuntime);
|
||||
}
|
||||
return app_adapter->DoClipboardGetText();
|
||||
}
|
||||
|
||||
void BaseFeatureSet::SetAppActive(bool active) {
|
||||
assert(InMainThread());
|
||||
g_core->platform->LowLevelDebugLog(
|
||||
"SetAppActive(" + std::to_string(active) + ")@"
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs()));
|
||||
|
||||
printf("APP ACTIVE %d\n", static_cast<int>(active));
|
||||
|
||||
// Issue a gentle warning if they are feeding us the same state twice in a
|
||||
// row; might imply faulty logic.
|
||||
if (app_active_set_ && app_active_ == active) {
|
||||
Log(LogLevel::kWarning, "SetAppActive called with state "
|
||||
+ std::to_string(active) + " twice in a row.");
|
||||
}
|
||||
app_active_set_ = true;
|
||||
app_active_ = active;
|
||||
}
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
@ -609,6 +609,31 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
|
||||
/// Start app systems in motion.
|
||||
void StartApp() override;
|
||||
|
||||
/// Set the app's active state. Should be called from the main thread.
|
||||
/// Generally called by the AppAdapter. Being inactive means the app
|
||||
/// experience is not front and center and thus it may want to throttle
|
||||
/// down its rendering rate, pause single play gameplay, etc. This does
|
||||
/// not, however, cause any extreme action such as halting event loops;
|
||||
/// use Suspend/Resume for that. And note that the app may still be
|
||||
/// visible while inactive, so it should not *completely* stop
|
||||
/// drawing/etc.
|
||||
void SetAppActive(bool active);
|
||||
|
||||
/// Put the app into a suspended state. Should be called from the main
|
||||
/// thread. Generally called by the AppAdapter. Suspends event loops,
|
||||
/// closes network sockets, etc. Generally corresponds to being
|
||||
/// backgrounded on mobile platforms. It is assumed that, as soon as this
|
||||
/// call returns, all engine work is finished and all threads can be
|
||||
/// immediately suspended by the OS without any problems.
|
||||
void SuspendApp();
|
||||
|
||||
/// Return the app to a running state from a suspended one. Can correspond
|
||||
/// to foregrounding on mobile, unminimizing on desktop, etc. Spins
|
||||
/// threads back up, re-opens network sockets, etc.
|
||||
void UnsuspendApp();
|
||||
|
||||
auto app_suspended() const { return app_suspended_; }
|
||||
|
||||
/// Issue a high level app quit request. Can be called from any thread and
|
||||
/// can be safely called repeatedly. If 'confirm' is true, a confirmation
|
||||
/// dialog will be presented if the environment and situation allows;
|
||||
@ -738,6 +763,22 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
|
||||
/// reported by the Python layer.
|
||||
auto GetV2AccountID() -> std::optional<std::string>;
|
||||
|
||||
/// Return whether clipboard operations are supported at all. This gets
|
||||
/// called when determining whether to display clipboard related UI
|
||||
/// elements/etc.
|
||||
auto ClipboardIsSupported() -> bool;
|
||||
|
||||
/// Return whether there is currently text on the clipboard.
|
||||
auto ClipboardHasText() -> bool;
|
||||
|
||||
/// Set current clipboard text. Raises an Exception if clipboard is
|
||||
/// unsupported.
|
||||
void ClipboardSetText(const std::string& text);
|
||||
|
||||
/// Return current text from the clipboard. Raises an Exception if
|
||||
/// clipboard is unsupported or if there's no text on the clipboard.
|
||||
auto ClipboardGetText() -> std::string;
|
||||
|
||||
// Const subsystems.
|
||||
AppAdapter* const app_adapter;
|
||||
AppConfig* const app_config;
|
||||
@ -774,10 +815,7 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
|
||||
// Non-const bits (fixme: clean up access to these).
|
||||
TouchInput* touch_input{};
|
||||
|
||||
// auto return_value() const { return return_value_; }
|
||||
// void set_return_value(int val) { return_value_ = val; }
|
||||
|
||||
// auto GetReturnValue() const -> int override;
|
||||
auto app_active() const { return app_active_; }
|
||||
|
||||
private:
|
||||
BaseFeatureSet();
|
||||
@ -789,8 +827,12 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
|
||||
AppMode* app_mode_;
|
||||
PlusSoftInterface* plus_soft_{};
|
||||
ClassicSoftInterface* classic_soft_{};
|
||||
|
||||
std::mutex shutdown_suppress_lock_;
|
||||
bool have_clipboard_is_supported_{};
|
||||
bool clipboard_is_supported_{};
|
||||
bool app_active_set_{};
|
||||
bool app_active_{true};
|
||||
bool app_suspended_{};
|
||||
bool shutdown_suppress_disallowed_{};
|
||||
bool tried_importing_plus_{};
|
||||
bool tried_importing_classic_{};
|
||||
@ -803,7 +845,6 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
|
||||
bool basn_log_behavior_{};
|
||||
bool server_wrapper_managed_{};
|
||||
int shutdown_suppress_count_{};
|
||||
// int return_value_{};
|
||||
};
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
@ -83,12 +83,12 @@ Graphics::~Graphics() = default;
|
||||
|
||||
void Graphics::OnAppStart() { assert(g_base->InLogicThread()); }
|
||||
|
||||
void Graphics::OnAppPause() {
|
||||
void Graphics::OnAppSuspend() {
|
||||
assert(g_base->InLogicThread());
|
||||
SetGyroEnabled(false);
|
||||
}
|
||||
|
||||
void Graphics::OnAppResume() {
|
||||
void Graphics::OnAppUnsuspend() {
|
||||
assert(g_base->InLogicThread());
|
||||
g_base->graphics->SetGyroEnabled(true);
|
||||
}
|
||||
@ -615,7 +615,7 @@ void Graphics::FadeScreen(bool to, millisecs_t time, PyObject* endcall) {
|
||||
Log(LogLevel::kWarning,
|
||||
"2 fades overlapping; running first fade-end-call early.");
|
||||
}
|
||||
fade_end_call_->ScheduleOnce();
|
||||
fade_end_call_->Schedule();
|
||||
fade_end_call_.Clear();
|
||||
}
|
||||
set_fade_start_on_next_draw_ = true;
|
||||
@ -993,7 +993,10 @@ void Graphics::DrawFades(FrameDef* frame_def) {
|
||||
// Guard against accidental fades that never fade back in.
|
||||
if (fade_ <= 0.0f && fade_out_) {
|
||||
millisecs_t faded_time = real_time - (fade_start_ + fade_time_);
|
||||
if (faded_time > 15000) {
|
||||
|
||||
// TEMP HACK - don't trigger this while inactive.
|
||||
// Need to make overall fade logic smarter.
|
||||
if (faded_time > 15000 && g_base->app_active()) {
|
||||
Log(LogLevel::kError, "FORCE-ENDING STUCK FADE");
|
||||
fade_out_ = false;
|
||||
fade_ = 1.0f;
|
||||
@ -1021,7 +1024,7 @@ void Graphics::DrawFades(FrameDef* frame_def) {
|
||||
} else {
|
||||
fade_ = 0;
|
||||
if (!was_done && fade_end_call_.Exists()) {
|
||||
fade_end_call_->ScheduleOnce();
|
||||
fade_end_call_->Schedule();
|
||||
fade_end_call_.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,8 +56,8 @@ class Graphics {
|
||||
Graphics();
|
||||
|
||||
void OnAppStart();
|
||||
void OnAppPause();
|
||||
void OnAppResume();
|
||||
void OnAppSuspend();
|
||||
void OnAppUnsuspend();
|
||||
void OnAppShutdown();
|
||||
void OnAppShutdownComplete();
|
||||
void OnScreenSizeChange();
|
||||
|
||||
@ -143,7 +143,7 @@ auto GraphicsServer::WaitForRenderFrameDef_() -> FrameDef* {
|
||||
// Spin and wait for a short bit for a frame_def to appear.
|
||||
while (true) {
|
||||
// Stop waiting if we can't/shouldn't render anyway.
|
||||
if (!renderer_ || shutting_down_ || g_base->app_adapter->app_suspended()) {
|
||||
if (!renderer_ || shutting_down_ || g_base->app_suspended()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@ -87,7 +87,7 @@ class TextGroup : public Object {
|
||||
Object::Ref<TextureAsset> os_texture_;
|
||||
std::vector<std::unique_ptr<TextMeshEntry>> entries_;
|
||||
std::string text_;
|
||||
bool big_;
|
||||
bool big_{};
|
||||
};
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
@ -155,27 +155,24 @@ void Input::AnnounceConnects_() {
|
||||
if (first_print && g_core->GetAppTimeSeconds() < 3.0) {
|
||||
first_print = false;
|
||||
|
||||
// Disabling this completely on Android for now; we often get large
|
||||
// numbers of devices there that aren't actually devices.
|
||||
bool do_print_initial_counts{!g_buildconfig.ostype_android()};
|
||||
|
||||
// If there's been several connected, just give a number.
|
||||
if (explicit_bool(do_print_initial_counts)) {
|
||||
if (newly_connected_controllers_.size() > 1) {
|
||||
std::string s =
|
||||
g_base->assets->GetResourceString("controllersDetectedText");
|
||||
Utils::StringReplaceOne(
|
||||
&s, "${COUNT}",
|
||||
std::to_string(newly_connected_controllers_.size()));
|
||||
ScreenMessage(s);
|
||||
} else {
|
||||
ScreenMessage(
|
||||
g_base->assets->GetResourceString("controllerDetectedText"));
|
||||
}
|
||||
if (newly_connected_controllers_.size() > 1) {
|
||||
std::string s =
|
||||
g_base->assets->GetResourceString("controllersDetectedText");
|
||||
Utils::StringReplaceOne(
|
||||
&s, "${COUNT}", std::to_string(newly_connected_controllers_.size()));
|
||||
ScreenMessage(s);
|
||||
} else {
|
||||
ScreenMessage(
|
||||
g_base->assets->GetResourceString("controllerDetectedText"));
|
||||
}
|
||||
|
||||
} else {
|
||||
// If there's been several connected, just give a number.
|
||||
if (newly_connected_controllers_.size() > 1) {
|
||||
for (auto&& s : newly_connected_controllers_) {
|
||||
Log(LogLevel::kInfo, "GOT CONTROLLER " + s);
|
||||
}
|
||||
std::string s =
|
||||
g_base->assets->GetResourceString("controllersConnectedText");
|
||||
Utils::StringReplaceOne(
|
||||
@ -193,7 +190,6 @@ void Input::AnnounceConnects_() {
|
||||
g_base->audio->PlaySound(g_base->assets->SysSound(SysSoundID::kGunCock));
|
||||
}
|
||||
}
|
||||
|
||||
newly_connected_controllers_.clear();
|
||||
}
|
||||
|
||||
@ -222,6 +218,14 @@ void Input::AnnounceDisconnects_() {
|
||||
|
||||
void Input::ShowStandardInputDeviceConnectedMessage_(InputDevice* j) {
|
||||
assert(g_base->InLogicThread());
|
||||
|
||||
// On Android we never show messages for initial input-devices; we often
|
||||
// get large numbers of strange virtual devices that aren't actually
|
||||
// controllers so this is more confusing than helpful.
|
||||
if (g_buildconfig.ostype_android() && g_core->GetAppTimeSeconds() < 3.0) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string suffix;
|
||||
suffix += j->GetPersistentIdentifier();
|
||||
suffix += j->GetDeviceExtraDescription();
|
||||
@ -516,9 +520,9 @@ void Input::OnAppStart() {
|
||||
}
|
||||
}
|
||||
|
||||
void Input::OnAppPause() { assert(g_base->InLogicThread()); }
|
||||
void Input::OnAppSuspend() { assert(g_base->InLogicThread()); }
|
||||
|
||||
void Input::OnAppResume() { assert(g_base->InLogicThread()); }
|
||||
void Input::OnAppUnsuspend() { assert(g_base->InLogicThread()); }
|
||||
|
||||
void Input::OnAppShutdown() { assert(g_base->InLogicThread()); }
|
||||
|
||||
@ -1239,7 +1243,14 @@ void Input::HandleSmoothMouseScroll_(const Vector2f& velocity, bool momentum) {
|
||||
}
|
||||
|
||||
void Input::PushMouseMotionEvent(const Vector2f& position) {
|
||||
assert(g_base->logic->event_loop());
|
||||
auto* loop = g_base->logic->event_loop();
|
||||
assert(loop);
|
||||
|
||||
// Don't overload it with events if it's stuck.
|
||||
if (!loop->CheckPushSafety()) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_base->logic->event_loop()->PushCall(
|
||||
[this, position] { HandleMouseMotion_(position); });
|
||||
}
|
||||
|
||||
@ -21,8 +21,8 @@ class Input {
|
||||
Input();
|
||||
|
||||
void OnAppStart();
|
||||
void OnAppPause();
|
||||
void OnAppResume();
|
||||
void OnAppSuspend();
|
||||
void OnAppUnsuspend();
|
||||
void OnAppShutdown();
|
||||
void OnAppShutdownComplete();
|
||||
void StepDisplayTime();
|
||||
|
||||
@ -376,8 +376,10 @@ auto RemoteAppServer::GetClient(int request_id, struct sockaddr* addr,
|
||||
Vector3f(1, 1, 1));
|
||||
});
|
||||
g_base->logic->event_loop()->PushCall([] {
|
||||
g_base->audio->PlaySound(
|
||||
g_base->assets->SysSound(SysSoundID::kGunCock));
|
||||
if (g_base->assets->asset_loads_allowed()) {
|
||||
g_base->audio->PlaySound(
|
||||
g_base->assets->SysSound(SysSoundID::kGunCock));
|
||||
}
|
||||
});
|
||||
}
|
||||
clients_[i].in_use = true;
|
||||
@ -426,9 +428,12 @@ auto RemoteAppServer::GetClient(int request_id, struct sockaddr* addr,
|
||||
});
|
||||
|
||||
g_base->logic->event_loop()->PushCall([] {
|
||||
g_base->audio->PlaySound(
|
||||
g_base->assets->SysSound(SysSoundID::kGunCock));
|
||||
if (g_base->assets->asset_loads_allowed()) {
|
||||
g_base->audio->PlaySound(
|
||||
g_base->assets->SysSound(SysSoundID::kGunCock));
|
||||
}
|
||||
});
|
||||
|
||||
std::string utf8 = Utils::GetValidUTF8(clients_[i].display_name, "rsgc1");
|
||||
clients_[i].joystick_ = Object::NewDeferred<JoystickInput>(
|
||||
-1, // not an sdl joystick
|
||||
|
||||
@ -48,9 +48,9 @@ void Logic::OnAppStart() {
|
||||
|
||||
// Stay informed when our event loop is pausing/unpausing.
|
||||
event_loop_->AddSuspendCallback(
|
||||
NewLambdaRunnableUnmanaged([this] { OnAppPause(); }));
|
||||
NewLambdaRunnableUnmanaged([this] { OnAppSuspend(); }));
|
||||
event_loop_->AddUnsuspendCallback(
|
||||
NewLambdaRunnableUnmanaged([this] { OnAppResume(); }));
|
||||
NewLambdaRunnableUnmanaged([this] { OnAppUnsuspend(); }));
|
||||
|
||||
// Running in a specific order here and should try to stick to it in
|
||||
// other OnAppXXX callbacks so any subsystem interdependencies behave
|
||||
@ -179,40 +179,40 @@ void Logic::OnInitialAppModeSet() {
|
||||
}
|
||||
}
|
||||
|
||||
void Logic::OnAppPause() {
|
||||
void Logic::OnAppSuspend() {
|
||||
assert(g_base->InLogicThread());
|
||||
assert(g_base->CurrentContext().IsEmpty());
|
||||
|
||||
// Note: keep these in opposite order of OnAppStart.
|
||||
g_base->python->OnAppPause();
|
||||
g_base->python->OnAppSuspend();
|
||||
if (g_base->HavePlus()) {
|
||||
g_base->plus()->OnAppPause();
|
||||
g_base->plus()->OnAppSuspend();
|
||||
}
|
||||
g_base->app_mode()->OnAppPause();
|
||||
g_base->ui->OnAppPause();
|
||||
g_base->input->OnAppPause();
|
||||
g_base->audio->OnAppPause();
|
||||
g_base->graphics->OnAppPause();
|
||||
g_base->platform->OnAppPause();
|
||||
g_base->app_adapter->OnAppPause();
|
||||
g_base->app_mode()->OnAppSuspend();
|
||||
g_base->ui->OnAppSuspend();
|
||||
g_base->input->OnAppSuspend();
|
||||
g_base->audio->OnAppSuspend();
|
||||
g_base->graphics->OnAppSuspend();
|
||||
g_base->platform->OnAppSuspend();
|
||||
g_base->app_adapter->OnAppSuspend();
|
||||
}
|
||||
|
||||
void Logic::OnAppResume() {
|
||||
void Logic::OnAppUnsuspend() {
|
||||
assert(g_base->InLogicThread());
|
||||
assert(g_base->CurrentContext().IsEmpty());
|
||||
|
||||
// Note: keep these in the same order as OnAppStart.
|
||||
g_base->app_adapter->OnAppResume();
|
||||
g_base->platform->OnAppResume();
|
||||
g_base->graphics->OnAppResume();
|
||||
g_base->audio->OnAppResume();
|
||||
g_base->input->OnAppResume();
|
||||
g_base->ui->OnAppResume();
|
||||
g_base->app_mode()->OnAppResume();
|
||||
g_base->app_adapter->OnAppUnsuspend();
|
||||
g_base->platform->OnAppUnsuspend();
|
||||
g_base->graphics->OnAppUnsuspend();
|
||||
g_base->audio->OnAppUnsuspend();
|
||||
g_base->input->OnAppUnsuspend();
|
||||
g_base->ui->OnAppUnsuspend();
|
||||
g_base->app_mode()->OnAppUnsuspend();
|
||||
if (g_base->HavePlus()) {
|
||||
g_base->plus()->OnAppResume();
|
||||
g_base->plus()->OnAppUnsuspend();
|
||||
}
|
||||
g_base->python->OnAppResume();
|
||||
g_base->python->OnAppUnsuspend();
|
||||
}
|
||||
|
||||
void Logic::Shutdown() {
|
||||
|
||||
@ -52,11 +52,11 @@ class Logic {
|
||||
|
||||
/// Called when our event-loop pauses. Informs Python and other
|
||||
/// subsystems.
|
||||
void OnAppPause();
|
||||
void OnAppSuspend();
|
||||
|
||||
/// Called when our event-loop resumes. Informs Python and other
|
||||
/// subsystems.
|
||||
void OnAppResume();
|
||||
void OnAppUnsuspend();
|
||||
|
||||
void OnAppShutdown();
|
||||
void OnAppShutdownComplete();
|
||||
|
||||
@ -25,7 +25,7 @@ void NetworkReader::SetPort(int port) {
|
||||
thread_ = new std::thread(RunThreadStatic_, this);
|
||||
}
|
||||
|
||||
void NetworkReader::OnAppPause() {
|
||||
void NetworkReader::OnAppSuspend() {
|
||||
assert(g_core->InMainThread());
|
||||
assert(!paused_);
|
||||
{
|
||||
@ -33,16 +33,14 @@ void NetworkReader::OnAppPause() {
|
||||
paused_ = true;
|
||||
}
|
||||
|
||||
// Ok now attempt to send a quick ping to ourself to wake us up so we can kill
|
||||
// our socket.
|
||||
// It's possible that we get suspended before port is set, so this could
|
||||
// still be -1.
|
||||
if (port4_ != -1) {
|
||||
PokeSelf_();
|
||||
} else {
|
||||
Log(LogLevel::kError, "NetworkReader port is -1 on pause");
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkReader::OnAppResume() {
|
||||
void NetworkReader::OnAppUnsuspend() {
|
||||
assert(g_core->InMainThread());
|
||||
assert(paused_);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user