mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-02-04 22:43:17 +08:00
v1.7.30
This commit is contained in:
parent
1c2cb0aecb
commit
115fd5eac6
88
.efrocachemap
generated
88
.efrocachemap
generated
@ -4060,50 +4060,50 @@
|
|||||||
"build/assets/windows/Win32/ucrtbased.dll": "2def5335207d41b21b9823f6805997f1",
|
"build/assets/windows/Win32/ucrtbased.dll": "2def5335207d41b21b9823f6805997f1",
|
||||||
"build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae",
|
"build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae",
|
||||||
"build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599",
|
"build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599",
|
||||||
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "dc1e98f8253f6e61583becda21c73162",
|
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "be3e38d0503d272d22a9226323803564",
|
||||||
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "c4e955cd85bc493fc03af74ce1062c66",
|
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "feea24dbb10aec67e1aa4d6c1d0928b5",
|
||||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "29c7e4a4accdb87c354e6181780654d4",
|
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "ac035cdcd1134ed3eefb50f2b6fee259",
|
||||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "1e254a7d528cbaa33cf5bce8e8c4fea7",
|
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "aa71c8cf97c379aa2cc93b3edb050167",
|
||||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "5b6792f0cadb52d82e483981b72def92",
|
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "afaf8cc0bd7e93439366a43a80debdbc",
|
||||||
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "d5bdea21bb4fe28e55de0dae42b05c09",
|
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "f787ba6f598297a6b43668949af0c8e2",
|
||||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "271c8808879bc09181f02c95787f6818",
|
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "f8317a770d865a1380bfef13011ae1cc",
|
||||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "40fe07bb55d239e1eabdc0976b46112d",
|
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "39afba54e9822911d8bfdf53602fcc8d",
|
||||||
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "ae0f96fc3364de8f6721d12e775e0ab2",
|
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "2fe24a441e96e882ce29cd55c886ee8e",
|
||||||
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "e2650e5017b27e84c398b894c768dee6",
|
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "4d068a5426f7e8814438a295f34d5356",
|
||||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "1a5d42f10fdd26ff5e81b07afa67c66e",
|
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "e315d748000555f280fd6a0e9575f799",
|
||||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "940c21f7293968fe88023e33607d36f2",
|
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "753a957f642173a1568725ff0b36bfce",
|
||||||
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "31f56d98b67c8894a2dfe73f4d4ed4c8",
|
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "833be86309d39cfb7f273c4c72ffdafc",
|
||||||
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "7b316468903c66cab07b0d8ae38b7bb7",
|
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "49ad9d968f4d615323a840f036caa36f",
|
||||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "42cd00bfbe63f750e9a064900582df48",
|
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "5795639a921b758c08a14ad09a8883d9",
|
||||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "045c0b16bf8ff0be028cefe5cfae1ee6",
|
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "db31fcbdeeb8f5ab794a1f9f1dfe7ede",
|
||||||
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "588711c1bb3cf986506b0a6a3d00fed9",
|
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "8aa9aa6b833d64d2e4a3689f6ce134fb",
|
||||||
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "a626549c279d83295e117835cc15050e",
|
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "7f6119e33678b934ac1c5f8002d8d6bc",
|
||||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "207ea740413855ede15da4ba1f304cf3",
|
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "639a01a7dcfd2f6db3023b610ad44d4a",
|
||||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "8516e63b6129b872ca59cdf5fade1c45",
|
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "e925ad3d39bb366f78c6e0f9da22d0cb",
|
||||||
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "add3863bc3c332a1196db0673fde5587",
|
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "4f5d3cafbea651078c1721684b61033a",
|
||||||
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "e3c41c240bb333fc53240f64d6e9583e",
|
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "7436c575aee1f9d112d41f18e2ae6b22",
|
||||||
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "add3863bc3c332a1196db0673fde5587",
|
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "4f5d3cafbea651078c1721684b61033a",
|
||||||
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "e3c41c240bb333fc53240f64d6e9583e",
|
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "7436c575aee1f9d112d41f18e2ae6b22",
|
||||||
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "0b0ec8be8c575beba2935fdc9aa03ce5",
|
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "3ffcb35eb71566fefb7d9ad2589b37b4",
|
||||||
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "04c21c4226944f71230420f9b399d1e4",
|
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "ce9de33efccb9aa1e301fe4f11e6b1c1",
|
||||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "0b0ec8be8c575beba2935fdc9aa03ce5",
|
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "3ffcb35eb71566fefb7d9ad2589b37b4",
|
||||||
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "04c21c4226944f71230420f9b399d1e4",
|
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "ce9de33efccb9aa1e301fe4f11e6b1c1",
|
||||||
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "e80ffa41dcc78dbd75baf89fadb096b4",
|
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "9d674b5e8a8357b9462a65359611ea45",
|
||||||
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "6514628d8ce1c046726de69fa0086613",
|
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "e6d1c0b9bf27c34968e512c5db8e7de5",
|
||||||
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "e80ffa41dcc78dbd75baf89fadb096b4",
|
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "9d674b5e8a8357b9462a65359611ea45",
|
||||||
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "6514628d8ce1c046726de69fa0086613",
|
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "e6d1c0b9bf27c34968e512c5db8e7de5",
|
||||||
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "9f4f5c5043d66fa3bdeb16b0599b5de4",
|
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "910b5e39fe4ba5bb849505c578efe3ec",
|
||||||
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "ddcb6a381ef5eaef6b1650cd94c498fc",
|
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "2d9e14f7cfe50b1dc51e5e9eae05b5fd",
|
||||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "a3124c863c4b80de5acc20a7c9d49492",
|
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "b83a67eeaed0fc99bf995767a8150e5d",
|
||||||
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "ddcb6a381ef5eaef6b1650cd94c498fc",
|
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "2d9e14f7cfe50b1dc51e5e9eae05b5fd",
|
||||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "155cfc2d5a62f02ab4490b24afe241e2",
|
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "8c6063999e8d77daf8bf8a33d045c737",
|
||||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "9c5b8303df63e5bdde524ae4ce654fa8",
|
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "e60f8d06f584bac8e54d920776a4d57d",
|
||||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "6895c7a2714c09e065ba6ac1f3860501",
|
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "89b61adb57d6a53740f302d01e107587",
|
||||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "a0a6f9d1afaacc9c762da68df3f9178c",
|
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "f347b87f6ee2167546edec41b750245c",
|
||||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "0ce476258657733e63c21be416d35574",
|
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "ed74c7fd0a854650675aa92366c2be3f",
|
||||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "e23b83abb5efcb2566e003be2c6a4b6e",
|
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "ac5046013922b1db4689c6ba653791c1",
|
||||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "364dd18a7b830ec1600827734c8ac0e6",
|
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "52c36bf974594558a33f7c6caa07b81e",
|
||||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "ab27a9c76a3706803283e09a43ecd92b",
|
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "27e287e1826677d1b4a480820e5e2f3a",
|
||||||
"src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c",
|
"src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c",
|
||||||
"src/assets/ba_data/python/babase/_mgen/enums.py": "28323912b56ec07701eda3d41a6a4101",
|
"src/assets/ba_data/python/babase/_mgen/enums.py": "28323912b56ec07701eda3d41a6a4101",
|
||||||
"src/ballistica/base/mgen/pyembed/binding_base.inc": "72bfed2cce8ff19741989dec28302f3f",
|
"src/ballistica/base/mgen/pyembed/binding_base.inc": "72bfed2cce8ff19741989dec28302f3f",
|
||||||
|
|||||||
16
CHANGELOG.md
16
CHANGELOG.md
@ -1,4 +1,4 @@
|
|||||||
### 1.7.30 (build 21657, api 8, 2023-12-02)
|
### 1.7.30 (build 21693, api 8, 2023-12-08)
|
||||||
- Continued work on the big 1.7.28 update.
|
- Continued work on the big 1.7.28 update.
|
||||||
- Got the Android version back up and running. There's been lots of cleanup and
|
- 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
|
simplification on the Android layer, cleaning out years of cruft. This should
|
||||||
@ -15,8 +15,14 @@
|
|||||||
- Bundled Android Python has been bumped to version 3.11.6.
|
- Bundled Android Python has been bumped to version 3.11.6.
|
||||||
- Android app suspend behavior has been revamped. The app should stay running
|
- Android app suspend behavior has been revamped. The app should stay running
|
||||||
more often and be quicker to respond when dialogs or other activities
|
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.
|
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
|
- (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)!).
|
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
|
- (build 21631) Fixes an issue where '^^^^^^^^^^^^^' lines in stack traces could
|
||||||
@ -31,6 +37,12 @@
|
|||||||
`from_window`. 2) In any call that can lead to you switching the main menu
|
`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
|
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.
|
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)
|
### 1.7.29 (build 21619, api 8, 2023-11-21)
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,7 @@ if TYPE_CHECKING:
|
|||||||
from babase._login import LoginAdapter, LoginInfo
|
from babase._login import LoginAdapter, LoginInfo
|
||||||
|
|
||||||
|
|
||||||
DEBUG_LOG = False
|
DEBUG_LOG = _babase.temp_testing()
|
||||||
|
|
||||||
|
|
||||||
class AccountV2Subsystem:
|
class AccountV2Subsystem:
|
||||||
@ -186,9 +186,10 @@ class AccountV2Subsystem:
|
|||||||
cfgkey = 'ImplicitLoginStates'
|
cfgkey = 'ImplicitLoginStates'
|
||||||
cfgdict = _babase.app.config.setdefault(cfgkey, {})
|
cfgdict = _babase.app.config.setdefault(cfgkey, {})
|
||||||
|
|
||||||
# Store which (if any) adapter is currently implicitly signed in.
|
# Store which (if any) adapter is currently implicitly signed
|
||||||
# Making the assumption there will only ever be one implicit
|
# in. Making the assumption there will only ever be one implicit
|
||||||
# adapter at a time; may need to update this if that changes.
|
# adapter at a time; may need to revisit this logic if that
|
||||||
|
# changes.
|
||||||
prev_state = cfgdict.get(login_type.value)
|
prev_state = cfgdict.get(login_type.value)
|
||||||
if state is None:
|
if state is None:
|
||||||
self._implicit_signed_in_adapter = None
|
self._implicit_signed_in_adapter = None
|
||||||
@ -296,9 +297,8 @@ class AccountV2Subsystem:
|
|||||||
# Consider this an 'explicit' sign in because the
|
# Consider this an 'explicit' sign in because the
|
||||||
# implicit-login state change presumably was triggered
|
# implicit-login state change presumably was triggered
|
||||||
# by some user action (signing in, signing out, or
|
# by some user action (signing in, signing out, or
|
||||||
# switching accounts via the back-end).
|
# switching accounts via the back-end). NOTE: should
|
||||||
# NOTE: should test case where we don't have
|
# test case where we don't have connectivity here.
|
||||||
# connectivity here.
|
|
||||||
if plus.cloud.is_connected():
|
if plus.cloud.is_connected():
|
||||||
if DEBUG_LOG:
|
if DEBUG_LOG:
|
||||||
logging.debug(
|
logging.debug(
|
||||||
|
|||||||
@ -17,7 +17,7 @@ if TYPE_CHECKING:
|
|||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
|
||||||
|
|
||||||
DEBUG_LOG = False
|
DEBUG_LOG = _babase.temp_testing()
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -145,7 +145,7 @@ class LoginAdapter:
|
|||||||
is actually being used by the app. It should therefore register
|
is actually being used by the app. It should therefore register
|
||||||
unlocked achievements, leaderboard scores, allow viewing native
|
unlocked achievements, leaderboard scores, allow viewing native
|
||||||
UIs, etc. When not active it should ignore everything and behave
|
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()
|
assert _babase.in_logic_thread()
|
||||||
del active # Unused.
|
del active # Unused.
|
||||||
|
|||||||
@ -52,7 +52,7 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
# Build number and version of the ballistica binary we expect to be
|
# Build number and version of the ballistica binary we expect to be
|
||||||
# using.
|
# using.
|
||||||
TARGET_BALLISTICA_BUILD = 21657
|
TARGET_BALLISTICA_BUILD = 21693
|
||||||
TARGET_BALLISTICA_VERSION = '1.7.30'
|
TARGET_BALLISTICA_VERSION = '1.7.30'
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -190,7 +190,7 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
|||||||
super().__del__()
|
super().__del__()
|
||||||
|
|
||||||
# If our UI is still up, kill it.
|
# 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():
|
with bui.ContextRef.empty():
|
||||||
bui.containerwidget(edit=self._root_ui, transition='out_left')
|
bui.containerwidget(edit=self._root_ui, transition='out_left')
|
||||||
|
|
||||||
|
|||||||
@ -209,8 +209,12 @@ class UIV1Subsystem(babase.AppSubsystem):
|
|||||||
|
|
||||||
def clear_main_menu_window(self, transition: str | None = None) -> None:
|
def clear_main_menu_window(self, transition: str | None = None) -> None:
|
||||||
"""Clear any existing 'main' window with the provided transition."""
|
"""Clear any existing 'main' window with the provided transition."""
|
||||||
|
assert transition is None or not transition.endswith('_in')
|
||||||
if self._main_menu_window:
|
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(
|
_bauiv1.containerwidget(
|
||||||
edit=self._main_menu_window, transition=transition
|
edit=self._main_menu_window, transition=transition
|
||||||
)
|
)
|
||||||
|
|||||||
@ -482,9 +482,12 @@ class PlayOptionsWindow(PopupWindow):
|
|||||||
cfg['Private Party Host Session Type'] = typename
|
cfg['Private Party Host Session Type'] = typename
|
||||||
bui.getsound('gunCocking').play()
|
bui.getsound('gunCocking').play()
|
||||||
assert bui.app.classic is not None
|
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(
|
bui.app.ui_v1.set_main_menu_window(
|
||||||
GatherWindow(transition='in_right').get_root_widget(),
|
GatherWindow(transition='in_right').get_root_widget(),
|
||||||
from_window=self.root_widget,
|
from_window=False, # Disable this test.
|
||||||
)
|
)
|
||||||
self._transition_out(transition='out_left')
|
self._transition_out(transition='out_left')
|
||||||
if self._delegate is not None:
|
if self._delegate is not None:
|
||||||
|
|||||||
@ -257,7 +257,7 @@ class ProfileBrowserWindow(bui.Window):
|
|||||||
EditProfileWindow(
|
EditProfileWindow(
|
||||||
existing_profile=None, in_main_menu=self._in_main_menu
|
existing_profile=None, in_main_menu=self._in_main_menu
|
||||||
).get_root_widget(),
|
).get_root_widget(),
|
||||||
from_window=self._root_widget,
|
from_window=self._root_widget if self._in_main_menu else False,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _delete_profile(self) -> None:
|
def _delete_profile(self) -> None:
|
||||||
@ -323,7 +323,7 @@ class ProfileBrowserWindow(bui.Window):
|
|||||||
EditProfileWindow(
|
EditProfileWindow(
|
||||||
self._selected_profile, in_main_menu=self._in_main_menu
|
self._selected_profile, in_main_menu=self._in_main_menu
|
||||||
).get_root_widget(),
|
).get_root_widget(),
|
||||||
from_window=self._root_widget,
|
from_window=self._root_widget if self._in_main_menu else False,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _select(self, name: str, index: int) -> None:
|
def _select(self, name: str, index: int) -> None:
|
||||||
|
|||||||
@ -151,8 +151,6 @@ void AppAdapter::NativeReviewRequest() {
|
|||||||
|
|
||||||
void AppAdapter::DoNativeReviewRequest() { FatalError("Fixme unimplemented."); }
|
void AppAdapter::DoNativeReviewRequest() { FatalError("Fixme unimplemented."); }
|
||||||
|
|
||||||
auto AppAdapter::ShouldSilenceAudioWhenInactive() -> bool const {
|
auto AppAdapter::ShouldSilenceAudioForInactive() -> bool const { return false; }
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ballistica::base
|
} // namespace ballistica::base
|
||||||
|
|||||||
@ -135,11 +135,14 @@ class AppAdapter {
|
|||||||
/// Return whether this AppAdapter supports max-fps controls for its display.
|
/// Return whether this AppAdapter supports max-fps controls for its display.
|
||||||
virtual auto SupportsMaxFPS() -> bool const;
|
virtual auto SupportsMaxFPS() -> bool const;
|
||||||
|
|
||||||
/// Return whether audio should be silenced when the app is inactive.
|
/// Return whether audio should be silenced when the app goes inactive. On
|
||||||
/// On Desktop systems it is generally normal to continue to hear things
|
/// Desktop systems it is generally normal to continue to hear things even
|
||||||
/// even if their windows are hidden, but on mobile we probably want to
|
/// if their windows are hidden, but on mobile we probably want to silence
|
||||||
/// silence our audio when phone calls, ads, etc. pop up over it.
|
/// our audio when phone calls, ads, etc. pop up over it. Note that this
|
||||||
virtual auto ShouldSilenceAudioWhenInactive() -> bool const;
|
/// 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
|
/// Return whether this platform supports soft-quit. A soft quit is
|
||||||
/// when the app is reset/backgrounded/etc. but remains running in case
|
/// when the app is reset/backgrounded/etc. but remains running in case
|
||||||
|
|||||||
@ -17,7 +17,8 @@
|
|||||||
#include <alc.h>
|
#include <alc.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if BA_OSTYPE_ANDROID
|
#if BA_OPENAL_IS_SOFT
|
||||||
|
#define AL_ALEXT_PROTOTYPES
|
||||||
#include <alext.h>
|
#include <alext.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@ -28,9 +28,12 @@ namespace ballistica::base {
|
|||||||
extern std::string g_rift_audio_device_name;
|
extern std::string g_rift_audio_device_name;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if BA_OSTYPE_ANDROID
|
#if BA_OPENAL_IS_SOFT
|
||||||
LPALCDEVICEPAUSESOFT alcDevicePauseSOFT{};
|
LPALCDEVICEPAUSESOFT alcDevicePauseSOFT{};
|
||||||
LPALCDEVICERESUMESOFT alcDeviceResumeSOFT{};
|
LPALCDEVICERESUMESOFT alcDeviceResumeSOFT{};
|
||||||
|
LPALCRESETDEVICESOFT alcResetDeviceSOFT{};
|
||||||
|
LPALEVENTCALLBACKSOFT alEventCallbackSOFT{};
|
||||||
|
LPALEVENTCONTROLSOFT alEventControlSOFT{};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const int kAudioProcessIntervalNormal{500 * 1000};
|
const int kAudioProcessIntervalNormal{500 * 1000};
|
||||||
@ -108,29 +111,24 @@ class AudioServer::ThreadSource_ : public Object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool looping_{};
|
|
||||||
std::unique_ptr<AudioSource> client_source_;
|
|
||||||
float fade_{1.0f};
|
|
||||||
float gain_{1.0f};
|
|
||||||
AudioServer* audio_server_{};
|
|
||||||
bool valid_{};
|
|
||||||
const Object::Ref<SoundAsset>* source_sound_{};
|
|
||||||
int id_{};
|
int id_{};
|
||||||
uint32_t play_count_{};
|
bool looping_{};
|
||||||
|
bool valid_{};
|
||||||
bool is_actually_playing_{};
|
bool is_actually_playing_{};
|
||||||
bool want_to_play_{};
|
bool want_to_play_{};
|
||||||
#if BA_ENABLE_AUDIO
|
|
||||||
ALuint source_{};
|
|
||||||
#endif
|
|
||||||
bool is_streamed_{};
|
bool is_streamed_{};
|
||||||
|
|
||||||
/// Whether we should be designated as "music" next time we play.
|
/// Whether we should be designated as "music" next time we play.
|
||||||
bool is_music_{};
|
bool is_music_{};
|
||||||
|
|
||||||
/// Whether currently playing as music.
|
/// Whether currently playing as music.
|
||||||
bool current_is_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
|
#if BA_ENABLE_AUDIO
|
||||||
|
ALuint source_{};
|
||||||
Object::Ref<AudioStreamer> streamer_;
|
Object::Ref<AudioStreamer> streamer_;
|
||||||
#endif
|
#endif
|
||||||
}; // ThreadSource
|
}; // ThreadSource
|
||||||
@ -156,6 +154,22 @@ void AudioServer::OnMainThreadStartApp() {
|
|||||||
event_loop_->PushCallSynchronous([this] { OnAppStartInThread_(); });
|
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_() {
|
void AudioServer::OnAppStartInThread_() {
|
||||||
assert(g_base->InAudioThread());
|
assert(g_base->InAudioThread());
|
||||||
|
|
||||||
@ -168,7 +182,7 @@ void AudioServer::OnAppStartInThread_() {
|
|||||||
|
|
||||||
// Bring up OpenAL stuff.
|
// 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
|
// On the rift build in vr mode we need to make sure we open the rift audio
|
||||||
// device.
|
// device.
|
||||||
@ -212,21 +226,42 @@ void AudioServer::OnAppStartInThread_() {
|
|||||||
"connected?");
|
"connected?");
|
||||||
}
|
}
|
||||||
impl_->alc_context = alcCreateContext(device, nullptr);
|
impl_->alc_context = alcCreateContext(device, nullptr);
|
||||||
BA_PRECONDITION(impl_->alc_context);
|
if (!impl_->alc_context) {
|
||||||
BA_PRECONDITION(alcMakeContextCurrent(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;
|
CHECK_AL_ERROR;
|
||||||
|
|
||||||
#if BA_OSTYPE_ANDROID
|
#if BA_OPENAL_IS_SOFT
|
||||||
if (alcIsExtensionPresent(device, "ALC_SOFT_pause_device")) {
|
// Currently assuming the pause/resume and reset extensions are present.
|
||||||
alcDevicePauseSOFT = reinterpret_cast<LPALCDEVICEPAUSESOFT>(
|
// if (alcIsExtensionPresent(device, "ALC_SOFT_pause_device")) {
|
||||||
alcGetProcAddress(device, "alcDevicePauseSOFT"));
|
alcDevicePauseSOFT = reinterpret_cast<LPALCDEVICEPAUSESOFT>(
|
||||||
BA_PRECONDITION_FATAL(alcDevicePauseSOFT != nullptr);
|
alcGetProcAddress(device, "alcDevicePauseSOFT"));
|
||||||
alcDeviceResumeSOFT = reinterpret_cast<LPALCDEVICERESUMESOFT>(
|
BA_PRECONDITION_FATAL(alcDevicePauseSOFT != nullptr);
|
||||||
alcGetProcAddress(device, "alcDeviceResumeSOFT"));
|
alcDeviceResumeSOFT = reinterpret_cast<LPALCDEVICERESUMESOFT>(
|
||||||
BA_PRECONDITION_FATAL(alcDeviceResumeSOFT != nullptr);
|
alcGetProcAddress(device, "alcDeviceResumeSOFT"));
|
||||||
} else {
|
BA_PRECONDITION_FATAL(alcDeviceResumeSOFT != nullptr);
|
||||||
FatalError("ALC_SOFT pause/resume functionality not found.");
|
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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,6 +295,7 @@ void AudioServer::OnAppStartInThread_() {
|
|||||||
// Now make available any stopped sources (should be all of them).
|
// Now make available any stopped sources (should be all of them).
|
||||||
UpdateAvailableSources_();
|
UpdateAvailableSources_();
|
||||||
|
|
||||||
|
last_started_playing_time_ = g_core->GetAppTimeSeconds();
|
||||||
#endif // BA_ENABLE_AUDIO
|
#endif // BA_ENABLE_AUDIO
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,23 +371,27 @@ void AudioServer::SetSuspended_(bool suspend) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Pause OpenALSoft.
|
// Pause OpenALSoft.
|
||||||
#if BA_OSTYPE_ANDROID
|
#if BA_OPENAL_IS_SOFT
|
||||||
BA_PRECONDITION_FATAL(alcDevicePauseSOFT != nullptr);
|
BA_PRECONDITION_FATAL(alcDevicePauseSOFT != nullptr);
|
||||||
BA_PRECONDITION_FATAL(impl_ != nullptr && impl_->alc_context != nullptr);
|
BA_PRECONDITION_FATAL(impl_ != nullptr && impl_->alc_context != nullptr);
|
||||||
auto* device = alcGetContextsDevice(impl_->alc_context);
|
auto* device = alcGetContextsDevice(impl_->alc_context);
|
||||||
BA_PRECONDITION_FATAL(device != nullptr);
|
BA_PRECONDITION_FATAL(device != nullptr);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
g_core->platform->LowLevelDebugLog(
|
||||||
|
"Calling alcDevicePauseSOFT at "
|
||||||
|
+ std::to_string(g_core->GetAppTimeSeconds()));
|
||||||
alcDevicePauseSOFT(device);
|
alcDevicePauseSOFT(device);
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
g_core->platform->LowLevelDebugLog(
|
Log(LogLevel::kError,
|
||||||
std::string("EXC pausing alcDevice: ")
|
"Error in alcDevicePauseSOFT at time "
|
||||||
+ g_core->platform->DemangleCXXSymbol(typeid(e).name()) + " "
|
+ std::to_string(g_core->GetAppTimeSeconds())
|
||||||
+ e.what());
|
+ "( playing since "
|
||||||
throw;
|
+ std::to_string(last_started_playing_time_)
|
||||||
|
+ "): " + g_core->platform->DemangleCXXSymbol(typeid(e).name())
|
||||||
|
+ " " + e.what());
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
g_core->platform->LowLevelDebugLog("UNKNOWN EXC pausing alcDevice");
|
Log(LogLevel::kError, "Unknown error in alcDevicePauseSOFT");
|
||||||
throw;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -373,25 +413,28 @@ void AudioServer::SetSuspended_(bool suspend) {
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// On android lets tell openal-soft to stop processing.
|
// With OpenALSoft lets tell openal-soft to resume processing.
|
||||||
#if BA_OSTYPE_ANDROID
|
#if BA_OPENAL_IS_SOFT
|
||||||
BA_PRECONDITION_FATAL(alcDeviceResumeSOFT != nullptr);
|
BA_PRECONDITION_FATAL(alcDeviceResumeSOFT != nullptr);
|
||||||
BA_PRECONDITION_FATAL(impl_ != nullptr && impl_->alc_context != nullptr);
|
BA_PRECONDITION_FATAL(impl_ != nullptr && impl_->alc_context != nullptr);
|
||||||
auto* device = alcGetContextsDevice(impl_->alc_context);
|
auto* device = alcGetContextsDevice(impl_->alc_context);
|
||||||
BA_PRECONDITION_FATAL(device != nullptr);
|
BA_PRECONDITION_FATAL(device != nullptr);
|
||||||
try {
|
try {
|
||||||
|
g_core->platform->LowLevelDebugLog(
|
||||||
|
"Calling alcDeviceResumeSOFT at "
|
||||||
|
+ std::to_string(g_core->GetAppTimeSeconds()));
|
||||||
alcDeviceResumeSOFT(device);
|
alcDeviceResumeSOFT(device);
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
g_core->platform->LowLevelDebugLog(
|
Log(LogLevel::kError,
|
||||||
std::string("EXC resuming alcDevice: ")
|
"Error in alcDeviceResumeSOFT at time "
|
||||||
+ g_core->platform->DemangleCXXSymbol(typeid(e).name()) + " "
|
+ std::to_string(g_core->GetAppTimeSeconds()) + ": "
|
||||||
+ e.what());
|
+ g_core->platform->DemangleCXXSymbol(typeid(e).name()) + " "
|
||||||
throw;
|
+ e.what());
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
g_core->platform->LowLevelDebugLog("UNKNOWN EXC resuming alcDevice");
|
Log(LogLevel::kError, "Unknown error in alcDeviceResumeSOFT");
|
||||||
throw;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
last_started_playing_time_ = g_core->GetAppTimeSeconds();
|
||||||
suspended_ = false;
|
suspended_ = false;
|
||||||
#if BA_ENABLE_AUDIO
|
#if BA_ENABLE_AUDIO
|
||||||
CHECK_AL_ERROR;
|
CHECK_AL_ERROR;
|
||||||
@ -478,8 +521,8 @@ void AudioServer::PushSourcePlayCall(uint32_t play_id,
|
|||||||
|
|
||||||
// Let's take this opportunity to pass on newly available sources.
|
// Let's take this opportunity to pass on newly available sources.
|
||||||
// This way the more things clients are playing, the more
|
// This way the more things clients are playing, the more
|
||||||
// tight our source availability checking gets (instead of solely relying on
|
// tight our source availability checking gets (instead of solely relying
|
||||||
// our periodic process() calls).
|
// on our periodic process() calls).
|
||||||
UpdateAvailableSources_();
|
UpdateAvailableSources_();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -686,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_() {
|
void AudioServer::Process_() {
|
||||||
assert(g_base->InAudioThread());
|
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;
|
||||||
|
|
||||||
// Only do real work if we're in normal running mode.
|
// Only do real work if we're in normal running mode.
|
||||||
if (!suspended_ && !shutting_down_) {
|
if (!suspended_ && !shutting_down_) {
|
||||||
|
ProcessDeviceDisconnects_(real_time_seconds);
|
||||||
|
|
||||||
// Do some loading...
|
// Do some loading...
|
||||||
have_pending_loads_ = g_base->assets->RunPendingAudioLoads();
|
have_pending_loads_ = g_base->assets->RunPendingAudioLoads();
|
||||||
|
|
||||||
@ -699,29 +788,28 @@ void AudioServer::Process_() {
|
|||||||
UpdateAvailableSources_();
|
UpdateAvailableSources_();
|
||||||
|
|
||||||
// Update our fading sound volumes.
|
// 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_();
|
ProcessSoundFades_();
|
||||||
last_sound_fade_process_time_ = real_time;
|
last_sound_fade_process_time_ = real_time_millisecs;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update streaming sources.
|
// Update streaming sources.
|
||||||
if (real_time - last_stream_process_time_ > 100) {
|
if (real_time_millisecs - last_stream_process_time_ > 100) {
|
||||||
last_stream_process_time_ = real_time;
|
last_stream_process_time_ = real_time_millisecs;
|
||||||
for (auto&& i : streaming_sources_) {
|
for (auto&& i : streaming_sources_) {
|
||||||
i->Update();
|
i->Update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the app has switched active/inactive state, update
|
// If the app has switched active/inactive state, update our volumes (we
|
||||||
// our volumes (we may silence our audio in these cases).
|
// may silence our audio in these cases).
|
||||||
auto app_active = g_base->app_active();
|
auto app_active = g_base->app_active();
|
||||||
if (app_active != app_active_) {
|
if (app_active != app_active_) {
|
||||||
app_active_ = app_active;
|
app_active_ = app_active;
|
||||||
if (g_base->app_adapter->ShouldSilenceAudioWhenInactive()) {
|
app_active_volume_ =
|
||||||
app_active_volume_ = app_active ? 1.0f : 0.0f;
|
(!app_active && g_base->app_adapter->ShouldSilenceAudioForInactive())
|
||||||
} else {
|
? 0.0f
|
||||||
app_active_volume_ = 1.0f;
|
: 1.0f;
|
||||||
}
|
|
||||||
for (auto&& i : sources_) {
|
for (auto&& i : sources_) {
|
||||||
i->UpdateVolume();
|
i->UpdateVolume();
|
||||||
}
|
}
|
||||||
@ -797,7 +885,8 @@ void AudioServer::ProcessSoundFades_() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AudioServer::FadeSoundOut(uint32_t play_id, uint32_t time) {
|
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(
|
sound_fade_nodes_.insert(
|
||||||
std::make_pair(play_id, SoundFadeNode_(play_id, time, true)));
|
std::make_pair(play_id, SoundFadeNode_(play_id, time, true)));
|
||||||
}
|
}
|
||||||
@ -882,8 +971,8 @@ void AudioServer::ThreadSource_::UpdateAvailability() {
|
|||||||
|
|
||||||
assert(g_base->InAudioThread());
|
assert(g_base->InAudioThread());
|
||||||
|
|
||||||
// If it's waiting to be picked up by a client or has pending client commands,
|
// If it's waiting to be picked up by a client or has pending client
|
||||||
// skip.
|
// commands, skip.
|
||||||
if (!client_source_->TryLock(6)) {
|
if (!client_source_->TryLock(6)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -895,10 +984,9 @@ void AudioServer::ThreadSource_::UpdateAvailability() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We consider ourselves busy if there's an active looping play command
|
// We consider ourselves busy if there's an active looping play command
|
||||||
// (regardless of its actual physical play state - music could be turned off,
|
// (regardless of its actual physical play state - music could be turned
|
||||||
// stuttering, etc.).
|
// off, stuttering, etc.). If it's non-looping, we check its play state and
|
||||||
// If it's non-looping, we check its play state and snatch it if it's not
|
// snatch it if it's not playing.
|
||||||
// playing.
|
|
||||||
bool busy;
|
bool busy;
|
||||||
if (looping_ || (is_streamed_ && streamer_.Exists() && streamer_->loops())) {
|
if (looping_ || (is_streamed_ && streamer_.Exists() && streamer_->loops())) {
|
||||||
busy = want_to_play_;
|
busy = want_to_play_;
|
||||||
@ -1244,16 +1332,6 @@ void AudioServer::PushSetSoundPitchCall(float val) {
|
|||||||
event_loop()->PushCall([this, val] { SetSoundPitch_(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(
|
void AudioServer::PushComponentUnloadCall(
|
||||||
const std::vector<Object::Ref<Asset>*>& components) {
|
const std::vector<Object::Ref<Asset>*>& components) {
|
||||||
event_loop()->PushCall([components] {
|
event_loop()->PushCall([components] {
|
||||||
|
|||||||
@ -67,6 +67,8 @@ class AudioServer {
|
|||||||
|
|
||||||
auto event_loop() const -> EventLoop* { return event_loop_; }
|
auto event_loop() const -> EventLoop* { return event_loop_; }
|
||||||
|
|
||||||
|
void OnDeviceDisconnected();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class ThreadSource_;
|
class ThreadSource_;
|
||||||
struct Impl_;
|
struct Impl_;
|
||||||
@ -90,6 +92,7 @@ class AudioServer {
|
|||||||
|
|
||||||
void Reset_();
|
void Reset_();
|
||||||
void Process_();
|
void Process_();
|
||||||
|
void ProcessDeviceDisconnects_(seconds_t real_time_seconds);
|
||||||
|
|
||||||
/// Send a component to the audio thread to delete.
|
/// Send a component to the audio thread to delete.
|
||||||
// void DeleteAssetComponent_(Asset* c);
|
// void DeleteAssetComponent_(Asset* c);
|
||||||
@ -122,7 +125,11 @@ class AudioServer {
|
|||||||
bool suspended_{};
|
bool suspended_{};
|
||||||
bool shutdown_completed_{};
|
bool shutdown_completed_{};
|
||||||
bool shutting_down_{};
|
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 shutdown_start_time_{};
|
||||||
|
seconds_t last_started_playing_time_{};
|
||||||
millisecs_t last_sound_fade_process_time_{};
|
millisecs_t last_sound_fade_process_time_{};
|
||||||
|
|
||||||
/// Indexed list of sources.
|
/// Indexed list of sources.
|
||||||
@ -146,8 +153,6 @@ class AudioServer {
|
|||||||
|
|
||||||
// Our list of sound media components to delete via the main thread.
|
// Our list of sound media components to delete via the main thread.
|
||||||
std::vector<const Object::Ref<SoundAsset>*> sound_ref_delete_list_;
|
std::vector<const Object::Ref<SoundAsset>*> sound_ref_delete_list_;
|
||||||
|
|
||||||
int al_source_count_{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ballistica::base
|
} // namespace ballistica::base
|
||||||
|
|||||||
@ -198,6 +198,8 @@ void BaseFeatureSet::StartApp() {
|
|||||||
BA_PRECONDITION(g_core->InMainThread());
|
BA_PRECONDITION(g_core->InMainThread());
|
||||||
BA_PRECONDITION(g_base);
|
BA_PRECONDITION(g_base);
|
||||||
|
|
||||||
|
auto start_time = g_core->GetAppTimeSeconds();
|
||||||
|
|
||||||
// Currently limiting this to once per process.
|
// Currently limiting this to once per process.
|
||||||
BA_PRECONDITION(!called_start_app_);
|
BA_PRECONDITION(!called_start_app_);
|
||||||
called_start_app_ = true;
|
called_start_app_ = true;
|
||||||
@ -248,6 +250,17 @@ void BaseFeatureSet::StartApp() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
g_core->LifecycleLog("start-app end (main thread)");
|
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() {
|
void BaseFeatureSet::SuspendApp() {
|
||||||
@ -890,8 +903,6 @@ void BaseFeatureSet::ShutdownSuppressDisallow() {
|
|||||||
shutdown_suppress_disallowed_ = true;
|
shutdown_suppress_disallowed_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// auto BaseFeatureSet::GetReturnValue() const -> int { return return_value(); }
|
|
||||||
|
|
||||||
void BaseFeatureSet::QuitApp(bool confirm, QuitType quit_type) {
|
void BaseFeatureSet::QuitApp(bool confirm, QuitType quit_type) {
|
||||||
// If they want a confirm dialog and we're able to present one, do that.
|
// If they want a confirm dialog and we're able to present one, do that.
|
||||||
if (confirm && !g_core->HeadlessMode() && !g_base->input->IsInputLocked()
|
if (confirm && !g_core->HeadlessMode() && !g_base->input->IsInputLocked()
|
||||||
|
|||||||
@ -87,7 +87,7 @@ class TextGroup : public Object {
|
|||||||
Object::Ref<TextureAsset> os_texture_;
|
Object::Ref<TextureAsset> os_texture_;
|
||||||
std::vector<std::unique_ptr<TextMeshEntry>> entries_;
|
std::vector<std::unique_ptr<TextMeshEntry>> entries_;
|
||||||
std::string text_;
|
std::string text_;
|
||||||
bool big_;
|
bool big_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ballistica::base
|
} // namespace ballistica::base
|
||||||
|
|||||||
@ -155,27 +155,24 @@ void Input::AnnounceConnects_() {
|
|||||||
if (first_print && g_core->GetAppTimeSeconds() < 3.0) {
|
if (first_print && g_core->GetAppTimeSeconds() < 3.0) {
|
||||||
first_print = false;
|
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 there's been several connected, just give a number.
|
||||||
if (explicit_bool(do_print_initial_counts)) {
|
if (newly_connected_controllers_.size() > 1) {
|
||||||
if (newly_connected_controllers_.size() > 1) {
|
std::string s =
|
||||||
std::string s =
|
g_base->assets->GetResourceString("controllersDetectedText");
|
||||||
g_base->assets->GetResourceString("controllersDetectedText");
|
Utils::StringReplaceOne(
|
||||||
Utils::StringReplaceOne(
|
&s, "${COUNT}", std::to_string(newly_connected_controllers_.size()));
|
||||||
&s, "${COUNT}",
|
ScreenMessage(s);
|
||||||
std::to_string(newly_connected_controllers_.size()));
|
} else {
|
||||||
ScreenMessage(s);
|
ScreenMessage(
|
||||||
} else {
|
g_base->assets->GetResourceString("controllerDetectedText"));
|
||||||
ScreenMessage(
|
|
||||||
g_base->assets->GetResourceString("controllerDetectedText"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// If there's been several connected, just give a number.
|
// If there's been several connected, just give a number.
|
||||||
if (newly_connected_controllers_.size() > 1) {
|
if (newly_connected_controllers_.size() > 1) {
|
||||||
|
for (auto&& s : newly_connected_controllers_) {
|
||||||
|
Log(LogLevel::kInfo, "GOT CONTROLLER " + s);
|
||||||
|
}
|
||||||
std::string s =
|
std::string s =
|
||||||
g_base->assets->GetResourceString("controllersConnectedText");
|
g_base->assets->GetResourceString("controllersConnectedText");
|
||||||
Utils::StringReplaceOne(
|
Utils::StringReplaceOne(
|
||||||
@ -193,7 +190,6 @@ void Input::AnnounceConnects_() {
|
|||||||
g_base->audio->PlaySound(g_base->assets->SysSound(SysSoundID::kGunCock));
|
g_base->audio->PlaySound(g_base->assets->SysSound(SysSoundID::kGunCock));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newly_connected_controllers_.clear();
|
newly_connected_controllers_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,6 +218,14 @@ void Input::AnnounceDisconnects_() {
|
|||||||
|
|
||||||
void Input::ShowStandardInputDeviceConnectedMessage_(InputDevice* j) {
|
void Input::ShowStandardInputDeviceConnectedMessage_(InputDevice* j) {
|
||||||
assert(g_base->InLogicThread());
|
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;
|
std::string suffix;
|
||||||
suffix += j->GetPersistentIdentifier();
|
suffix += j->GetPersistentIdentifier();
|
||||||
suffix += j->GetDeviceExtraDescription();
|
suffix += j->GetDeviceExtraDescription();
|
||||||
@ -1239,7 +1243,14 @@ void Input::HandleSmoothMouseScroll_(const Vector2f& velocity, bool momentum) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Input::PushMouseMotionEvent(const Vector2f& position) {
|
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(
|
g_base->logic->event_loop()->PushCall(
|
||||||
[this, position] { HandleMouseMotion_(position); });
|
[this, position] { HandleMouseMotion_(position); });
|
||||||
}
|
}
|
||||||
|
|||||||
@ -376,8 +376,10 @@ auto RemoteAppServer::GetClient(int request_id, struct sockaddr* addr,
|
|||||||
Vector3f(1, 1, 1));
|
Vector3f(1, 1, 1));
|
||||||
});
|
});
|
||||||
g_base->logic->event_loop()->PushCall([] {
|
g_base->logic->event_loop()->PushCall([] {
|
||||||
g_base->audio->PlaySound(
|
if (g_base->assets->asset_loads_allowed()) {
|
||||||
g_base->assets->SysSound(SysSoundID::kGunCock));
|
g_base->audio->PlaySound(
|
||||||
|
g_base->assets->SysSound(SysSoundID::kGunCock));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
clients_[i].in_use = true;
|
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->logic->event_loop()->PushCall([] {
|
||||||
g_base->audio->PlaySound(
|
if (g_base->assets->asset_loads_allowed()) {
|
||||||
g_base->assets->SysSound(SysSoundID::kGunCock));
|
g_base->audio->PlaySound(
|
||||||
|
g_base->assets->SysSound(SysSoundID::kGunCock));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
std::string utf8 = Utils::GetValidUTF8(clients_[i].display_name, "rsgc1");
|
std::string utf8 = Utils::GetValidUTF8(clients_[i].display_name, "rsgc1");
|
||||||
clients_[i].joystick_ = Object::NewDeferred<JoystickInput>(
|
clients_[i].joystick_ = Object::NewDeferred<JoystickInput>(
|
||||||
-1, // not an sdl joystick
|
-1, // not an sdl joystick
|
||||||
|
|||||||
@ -33,12 +33,10 @@ void NetworkReader::OnAppSuspend() {
|
|||||||
paused_ = true;
|
paused_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ok now attempt to send a quick ping to ourself to wake us up so we can kill
|
// It's possible that we get suspended before port is set, so this could
|
||||||
// our socket.
|
// still be -1.
|
||||||
if (port4_ != -1) {
|
if (port4_ != -1) {
|
||||||
PokeSelf_();
|
PokeSelf_();
|
||||||
} else {
|
|
||||||
Log(LogLevel::kError, "NetworkReader port is -1 on pause");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1788,6 +1788,32 @@ static PyMethodDef PyNativeReviewRequestDef = {
|
|||||||
"\n"
|
"\n"
|
||||||
"(internal)",
|
"(internal)",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ------------------------------- temp_testing --------------------------------
|
||||||
|
|
||||||
|
static auto PyTempTesting(PyObject* self) -> PyObject* {
|
||||||
|
BA_PYTHON_TRY;
|
||||||
|
|
||||||
|
std::string devstr = g_core->platform->GetDeviceName() + " "
|
||||||
|
+ g_core->platform->GetOSVersionString();
|
||||||
|
if (devstr == "samsung SM-N950F 7.1.1") {
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
}
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
|
||||||
|
BA_PYTHON_CATCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyMethodDef PyTempTestingDef = {
|
||||||
|
"temp_testing", // name
|
||||||
|
(PyCFunction)PyTempTesting, // method
|
||||||
|
METH_NOARGS, // flags
|
||||||
|
|
||||||
|
"temp_testing() -> bool\n"
|
||||||
|
"\n"
|
||||||
|
"(internal)",
|
||||||
|
};
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
auto PythonMethodsMisc::GetMethods() -> std::vector<PyMethodDef> {
|
auto PythonMethodsMisc::GetMethods() -> std::vector<PyMethodDef> {
|
||||||
@ -1856,6 +1882,7 @@ auto PythonMethodsMisc::GetMethods() -> std::vector<PyMethodDef> {
|
|||||||
PyUsingGameCenterDef,
|
PyUsingGameCenterDef,
|
||||||
PyNativeReviewRequestSupportedDef,
|
PyNativeReviewRequestSupportedDef,
|
||||||
PyNativeReviewRequestDef,
|
PyNativeReviewRequestDef,
|
||||||
|
PyTempTestingDef,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -236,9 +236,6 @@ void UI::MainMenuPress_(InputDevice* device) {
|
|||||||
assert(g_base->InLogicThread());
|
assert(g_base->InLogicThread());
|
||||||
if (auto* ui_delegate = g_base->ui->delegate()) {
|
if (auto* ui_delegate = g_base->ui->delegate()) {
|
||||||
ui_delegate->DoHandleDeviceMenuPress(device);
|
ui_delegate->DoHandleDeviceMenuPress(device);
|
||||||
} else {
|
|
||||||
Log(LogLevel::kWarning,
|
|
||||||
"UI::MainMenuPress called without ui_v1 present; unexpected.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -158,10 +158,10 @@ class CoreFeatureSet {
|
|||||||
bool v1_cloud_log_full{};
|
bool v1_cloud_log_full{};
|
||||||
int master_server_source{};
|
int master_server_source{};
|
||||||
std::vector<EventLoop*> suspendable_event_loops;
|
std::vector<EventLoop*> suspendable_event_loops;
|
||||||
std::mutex v1_cloud_log_mutex;
|
|
||||||
std::string v1_cloud_log;
|
|
||||||
std::mutex thread_name_map_mutex;
|
std::mutex thread_name_map_mutex;
|
||||||
std::unordered_map<std::thread::id, std::string> thread_name_map;
|
std::unordered_map<std::thread::id, std::string> thread_name_map;
|
||||||
|
std::mutex v1_cloud_log_mutex;
|
||||||
|
std::string v1_cloud_log;
|
||||||
|
|
||||||
#if BA_DEBUG_BUILD
|
#if BA_DEBUG_BUILD
|
||||||
std::mutex object_list_mutex;
|
std::mutex object_list_mutex;
|
||||||
|
|||||||
@ -39,7 +39,7 @@ auto main(int argc, char** argv) -> int {
|
|||||||
namespace ballistica {
|
namespace ballistica {
|
||||||
|
|
||||||
// These are set automatically via script; don't modify them here.
|
// These are set automatically via script; don't modify them here.
|
||||||
const int kEngineBuildNumber = 21657;
|
const int kEngineBuildNumber = 21693;
|
||||||
const char* kEngineVersion = "1.7.30";
|
const char* kEngineVersion = "1.7.30";
|
||||||
const int kEngineApiVersion = 8;
|
const int kEngineApiVersion = 8;
|
||||||
|
|
||||||
@ -53,6 +53,8 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int {
|
|||||||
core::BaseSoftInterface* l_base{};
|
core::BaseSoftInterface* l_base{};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
auto time1 = core::CorePlatform::GetCurrentMillisecs();
|
||||||
|
|
||||||
// Even at the absolute start of execution we should be able to
|
// Even at the absolute start of execution we should be able to
|
||||||
// reasonably log errors. Set env var BA_CRASH_TEST=1 to test this.
|
// reasonably log errors. Set env var BA_CRASH_TEST=1 to test this.
|
||||||
if (const char* crashenv = getenv("BA_CRASH_TEST")) {
|
if (const char* crashenv = getenv("BA_CRASH_TEST")) {
|
||||||
@ -66,6 +68,8 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int {
|
|||||||
// import it first thing even if we don't explicitly use it.
|
// import it first thing even if we don't explicitly use it.
|
||||||
l_core = core::CoreFeatureSet::Import(&core_config);
|
l_core = core::CoreFeatureSet::Import(&core_config);
|
||||||
|
|
||||||
|
auto time2 = core::CorePlatform::GetCurrentMillisecs();
|
||||||
|
|
||||||
// If a command was passed, simply run it and exit. We want to act
|
// If a command was passed, simply run it and exit. We want to act
|
||||||
// simply as a Python interpreter in that case; we don't do any
|
// simply as a Python interpreter in that case; we don't do any
|
||||||
// environment setup (aside from the bits core does automatically such
|
// environment setup (aside from the bits core does automatically such
|
||||||
@ -90,6 +94,8 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int {
|
|||||||
// those modules get loaded from in the first place.
|
// those modules get loaded from in the first place.
|
||||||
l_core->python->MonolithicModeBaEnvConfigure();
|
l_core->python->MonolithicModeBaEnvConfigure();
|
||||||
|
|
||||||
|
auto time3 = core::CorePlatform::GetCurrentMillisecs();
|
||||||
|
|
||||||
// We need the base feature-set to run a full app but we don't have a hard
|
// We need the base feature-set to run a full app but we don't have a hard
|
||||||
// dependency to it. Let's see if it's available.
|
// dependency to it. Let's see if it's available.
|
||||||
l_base = l_core->SoftImportBase();
|
l_base = l_core->SoftImportBase();
|
||||||
@ -97,6 +103,8 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int {
|
|||||||
FatalError("Base module unavailable; can't run app.");
|
FatalError("Base module unavailable; can't run app.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto time4 = core::CorePlatform::GetCurrentMillisecs();
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// Phase 2: "The pieces are moving."
|
// Phase 2: "The pieces are moving."
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
@ -113,6 +121,23 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int {
|
|||||||
// until the app exits (or we return from this function and let the
|
// until the app exits (or we return from this function and let the
|
||||||
// environment do that part).
|
// environment do that part).
|
||||||
|
|
||||||
|
// Make noise if it takes us too long to get to this point.
|
||||||
|
auto time5 = core::CorePlatform::GetCurrentMillisecs();
|
||||||
|
auto total_duration = time5 - time1;
|
||||||
|
if (total_duration > 5000) {
|
||||||
|
auto core_import_duration = time2 - time1;
|
||||||
|
auto env_config_duration = time3 - time2;
|
||||||
|
auto base_import_duration = time4 - time3;
|
||||||
|
auto start_app_duration = time5 - time4;
|
||||||
|
Log(LogLevel::kWarning,
|
||||||
|
"MonolithicMain took too long (" + std::to_string(total_duration)
|
||||||
|
+ " ms; " + std::to_string(core_import_duration)
|
||||||
|
+ " core-import, " + std::to_string(env_config_duration)
|
||||||
|
+ " env-config, " + std::to_string(base_import_duration)
|
||||||
|
+ " base-import, " + std::to_string(start_app_duration)
|
||||||
|
+ " start-app).");
|
||||||
|
}
|
||||||
|
|
||||||
if (l_base->AppManagesMainThreadEventLoop()) {
|
if (l_base->AppManagesMainThreadEventLoop()) {
|
||||||
// In environments where we control the event loop, do that.
|
// In environments where we control the event loop, do that.
|
||||||
l_base->RunAppToCompletion();
|
l_base->RunAppToCompletion();
|
||||||
@ -130,20 +155,20 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int {
|
|||||||
std::string error_msg =
|
std::string error_msg =
|
||||||
std::string("Unhandled exception in MonolithicMain(): ") + exc.what();
|
std::string("Unhandled exception in MonolithicMain(): ") + exc.what();
|
||||||
|
|
||||||
// Let the user and/or master-server know we're dying.
|
// Let the user and/or master-server know what killed us.
|
||||||
FatalError::ReportFatalError(error_msg, true);
|
FatalError::ReportFatalError(error_msg, true);
|
||||||
|
|
||||||
// Exiting the app via an exception leads to crash reports on various
|
// Exiting the app via an exception tends to lead to crash reports. If
|
||||||
// platforms. If it seems we're not on an official live build then we'd
|
// it seems we're not on an official live build then we'd rather just
|
||||||
// rather just exit cleanly with an error code and avoid polluting crash
|
// exit cleanly with an error code and avoid polluting crash report logs
|
||||||
// report logs with reports from dev builds.
|
// with reports from dev builds.
|
||||||
bool try_to_exit_cleanly = !(l_base && l_base->IsUnmodifiedBlessedBuild());
|
bool try_to_exit_cleanly = !(l_base && l_base->IsUnmodifiedBlessedBuild());
|
||||||
|
|
||||||
// If this is true it means the app is handling things (showing a fatal
|
// If this returns true, it means the app is handling things (showing a
|
||||||
// error dialog, etc.) and it's out of our hands.
|
// fatal error dialog, etc.) and it's out of our hands.
|
||||||
bool handled = FatalError::HandleFatalError(try_to_exit_cleanly, true);
|
bool handled = FatalError::HandleFatalError(try_to_exit_cleanly, true);
|
||||||
|
|
||||||
// Do the default thing if it's not been handled.
|
// If it's not been handled, take the app down ourself.
|
||||||
if (!handled) {
|
if (!handled) {
|
||||||
if (try_to_exit_cleanly) {
|
if (try_to_exit_cleanly) {
|
||||||
exit(1);
|
exit(1);
|
||||||
@ -155,22 +180,95 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A way to do the same as above except in an incremental manner. This can
|
||||||
|
// be used to avoid app-not-responding reports on slow devices by
|
||||||
|
// interleaving engine init steps with other event processing.
|
||||||
|
class IncrementalInitRunner_ {
|
||||||
|
public:
|
||||||
|
explicit IncrementalInitRunner_(const core::CoreConfig* config)
|
||||||
|
: config_(*config) {}
|
||||||
|
auto Process() -> bool {
|
||||||
|
if (zombie_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
switch (step_) {
|
||||||
|
case 0:
|
||||||
|
core_ = core::CoreFeatureSet::Import(&config_);
|
||||||
|
step_++;
|
||||||
|
return false;
|
||||||
|
case 1:
|
||||||
|
core_->python->MonolithicModeBaEnvConfigure();
|
||||||
|
step_++;
|
||||||
|
return false;
|
||||||
|
case 2:
|
||||||
|
base_ = core_->SoftImportBase();
|
||||||
|
if (!base_) {
|
||||||
|
FatalError("Base module unavailable; can't run app.");
|
||||||
|
}
|
||||||
|
step_++;
|
||||||
|
return false;
|
||||||
|
case 3:
|
||||||
|
base_->StartApp();
|
||||||
|
Python::PermanentlyReleaseGIL();
|
||||||
|
step_++;
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (const std::exception& exc) {
|
||||||
|
std::string error_msg =
|
||||||
|
std::string("Unhandled exception in MonolithicMain(): ") + exc.what();
|
||||||
|
|
||||||
|
// Let the user and/or master-server know what killed us.
|
||||||
|
FatalError::ReportFatalError(error_msg, true);
|
||||||
|
|
||||||
|
// Exiting the app via an exception tends to lead to crash reports. If
|
||||||
|
// it seems we're not on an official live build then we'd rather just
|
||||||
|
// exit cleanly with an error code and avoid polluting crash report logs
|
||||||
|
// with reports from dev builds.
|
||||||
|
bool try_to_exit_cleanly = !(base_ && base_->IsUnmodifiedBlessedBuild());
|
||||||
|
|
||||||
|
// If this returns true, it means the app is handling things (showing a
|
||||||
|
// fatal error dialog, etc.) and it's out of our hands.
|
||||||
|
bool handled = FatalError::HandleFatalError(try_to_exit_cleanly, true);
|
||||||
|
|
||||||
|
// If it's not been handled, take the app down ourself.
|
||||||
|
if (!handled) {
|
||||||
|
if (try_to_exit_cleanly) {
|
||||||
|
exit(1);
|
||||||
|
} else {
|
||||||
|
throw; // Crash report here we come!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Just go into vegetable mode so hopefully the handler can do its
|
||||||
|
// thing.
|
||||||
|
zombie_ = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int step_{};
|
||||||
|
bool zombie_{};
|
||||||
|
core::CoreConfig config_;
|
||||||
|
core::CoreFeatureSet* core_{};
|
||||||
|
core::BaseSoftInterface* base_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
static IncrementalInitRunner_* g_incremental_init_runner_{};
|
||||||
|
|
||||||
|
auto MonolithicMainIncremental(const core::CoreConfig* config) -> bool {
|
||||||
|
if (g_incremental_init_runner_ == nullptr) {
|
||||||
|
g_incremental_init_runner_ = new IncrementalInitRunner_(config);
|
||||||
|
}
|
||||||
|
return g_incremental_init_runner_->Process();
|
||||||
|
}
|
||||||
|
|
||||||
#endif // BA_MONOLITHIC_BUILD
|
#endif // BA_MONOLITHIC_BUILD
|
||||||
|
|
||||||
void FatalError(const std::string& message) {
|
void FatalError(const std::string& message) {
|
||||||
// Let the user and/or master-server know we're dying.
|
FatalError::DoFatalError(message);
|
||||||
FatalError::ReportFatalError(message, false);
|
|
||||||
|
|
||||||
// Exiting the app via an exception leads to crash reports on various
|
|
||||||
// platforms. If it seems we're not on an official live build then we'd
|
|
||||||
// rather just exit cleanly with an error code and avoid polluting crash
|
|
||||||
// report logs with reports from dev builds.
|
|
||||||
bool try_to_exit_cleanly =
|
|
||||||
!(core::g_base_soft && core::g_base_soft->IsUnmodifiedBlessedBuild());
|
|
||||||
bool handled = FatalError::HandleFatalError(try_to_exit_cleanly, false);
|
|
||||||
if (!handled) {
|
|
||||||
throw Exception("A fatal error occurred.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Log(LogLevel level, const std::string& msg) { Logging::Log(level, msg); }
|
void Log(LogLevel level, const std::string& msg) { Logging::Log(level, msg); }
|
||||||
|
|||||||
@ -68,6 +68,11 @@ class CoreConfig;
|
|||||||
/// Entry point for standard monolithic builds. Handles all initing and
|
/// Entry point for standard monolithic builds. Handles all initing and
|
||||||
/// running.
|
/// running.
|
||||||
auto MonolithicMain(const core::CoreConfig& config) -> int;
|
auto MonolithicMain(const core::CoreConfig& config) -> int;
|
||||||
|
|
||||||
|
/// Special alternate version of MonolithicMain which breaks its work into
|
||||||
|
/// pieces; used to reduce app-not-responding reports from slow Android
|
||||||
|
/// devices. Call this repeatedly until it returns true;
|
||||||
|
auto MonolithicMainIncremental(const core::CoreConfig* config) -> bool;
|
||||||
#endif // BA_MONOLITHIC_BUILD
|
#endif // BA_MONOLITHIC_BUILD
|
||||||
|
|
||||||
// Print a momentary message on the screen.
|
// Print a momentary message on the screen.
|
||||||
|
|||||||
@ -342,47 +342,46 @@ void EventLoop::GetThreadMessages_(std::list<ThreadMessage_>* messages) {
|
|||||||
void EventLoop::BootstrapThread_() {
|
void EventLoop::BootstrapThread_() {
|
||||||
assert(!bootstrapped_);
|
assert(!bootstrapped_);
|
||||||
thread_id_ = std::this_thread::get_id();
|
thread_id_ = std::this_thread::get_id();
|
||||||
const char* name;
|
|
||||||
const char* id_string;
|
const char* id_string;
|
||||||
|
|
||||||
switch (identifier_) {
|
switch (identifier_) {
|
||||||
case EventLoopID::kLogic:
|
case EventLoopID::kLogic:
|
||||||
name = "logic";
|
name_ = "logic";
|
||||||
id_string = "ballistica logic";
|
id_string = "ballistica logic";
|
||||||
break;
|
break;
|
||||||
case EventLoopID::kStdin:
|
case EventLoopID::kStdin:
|
||||||
name = "stdin";
|
name_ = "stdin";
|
||||||
id_string = "ballistica stdin";
|
id_string = "ballistica stdin";
|
||||||
break;
|
break;
|
||||||
case EventLoopID::kAssets:
|
case EventLoopID::kAssets:
|
||||||
name = "assets";
|
name_ = "assets";
|
||||||
id_string = "ballistica assets";
|
id_string = "ballistica assets";
|
||||||
break;
|
break;
|
||||||
case EventLoopID::kFileOut:
|
case EventLoopID::kFileOut:
|
||||||
name = "fileout";
|
name_ = "fileout";
|
||||||
id_string = "ballistica file-out";
|
id_string = "ballistica file-out";
|
||||||
break;
|
break;
|
||||||
case EventLoopID::kMain:
|
case EventLoopID::kMain:
|
||||||
name = "main";
|
name_ = "main";
|
||||||
id_string = "ballistica main";
|
id_string = "ballistica main";
|
||||||
break;
|
break;
|
||||||
case EventLoopID::kAudio:
|
case EventLoopID::kAudio:
|
||||||
name = "audio";
|
name_ = "audio";
|
||||||
id_string = "ballistica audio";
|
id_string = "ballistica audio";
|
||||||
break;
|
break;
|
||||||
case EventLoopID::kBGDynamics:
|
case EventLoopID::kBGDynamics:
|
||||||
name = "bgdynamics";
|
name_ = "bgdynamics";
|
||||||
id_string = "ballistica bg-dynamics";
|
id_string = "ballistica bg-dynamics";
|
||||||
break;
|
break;
|
||||||
case EventLoopID::kNetworkWrite:
|
case EventLoopID::kNetworkWrite:
|
||||||
name = "networkwrite";
|
name_ = "networkwrite";
|
||||||
id_string = "ballistica network-write";
|
id_string = "ballistica network-write";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw Exception();
|
throw Exception();
|
||||||
}
|
}
|
||||||
assert(name && id_string);
|
assert(!name_.empty() && id_string);
|
||||||
SetInternalThreadName_(name);
|
SetInternalThreadName_(name_);
|
||||||
|
|
||||||
// Note: we currently don't do this for our main thread because it
|
// Note: we currently don't do this for our main thread because it
|
||||||
// changes the process name we see in top/etc. Should look into that.
|
// changes the process name we see in top/etc. Should look into that.
|
||||||
@ -552,8 +551,7 @@ void EventLoop::PushThreadMessage_(const ThreadMessage_& t) {
|
|||||||
if (!sent_error) {
|
if (!sent_error) {
|
||||||
sent_error = true;
|
sent_error = true;
|
||||||
log_entries.emplace_back(
|
log_entries.emplace_back(
|
||||||
LogLevel::kError,
|
LogLevel::kError, "ThreadMessage list > 1000 in thread: " + name_);
|
||||||
"ThreadMessage list > 1000 in thread: " + CurrentThreadName());
|
|
||||||
|
|
||||||
LogThreadMessageTally_(&log_entries);
|
LogThreadMessageTally_(&log_entries);
|
||||||
}
|
}
|
||||||
@ -561,8 +559,7 @@ void EventLoop::PushThreadMessage_(const ThreadMessage_& t) {
|
|||||||
|
|
||||||
// Prevent runaway mem usage if the list gets out of control.
|
// Prevent runaway mem usage if the list gets out of control.
|
||||||
if (thread_messages_.size() > 10000) {
|
if (thread_messages_.size() > 10000) {
|
||||||
FatalError("ThreadMessage list > 10000 in thread: "
|
FatalError("ThreadMessage list > 10000 in thread: " + name_);
|
||||||
+ CurrentThreadName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlock thread-message list and inform thread that there's something
|
// Unlock thread-message list and inform thread that there's something
|
||||||
|
|||||||
@ -97,6 +97,8 @@ class EventLoop {
|
|||||||
auto suspended() { return suspended_; }
|
auto suspended() { return suspended_; }
|
||||||
auto done() -> bool { return done_; }
|
auto done() -> bool { return done_; }
|
||||||
|
|
||||||
|
auto name() const { return name_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ThreadMessage_ {
|
struct ThreadMessage_ {
|
||||||
enum class Type { kShutdown = 999, kRunnable, kSuspend, kUnsuspend };
|
enum class Type { kShutdown = 999, kRunnable, kSuspend, kUnsuspend };
|
||||||
@ -149,13 +151,6 @@ class EventLoop {
|
|||||||
|
|
||||||
void BootstrapThread_();
|
void BootstrapThread_();
|
||||||
|
|
||||||
// void LoopUpkeep_(bool single_cycle);
|
|
||||||
|
|
||||||
// FIXME: Should generalize this to some sort of PlatformThreadData class.
|
|
||||||
#if BA_XCODE_BUILD
|
|
||||||
// void* auto_release_pool_{};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
EventLoopID identifier_{EventLoopID::kInvalid};
|
EventLoopID identifier_{EventLoopID::kInvalid};
|
||||||
ThreadSource source_{};
|
ThreadSource source_{};
|
||||||
bool bootstrapped_{};
|
bool bootstrapped_{};
|
||||||
@ -173,6 +168,7 @@ class EventLoop {
|
|||||||
std::mutex thread_message_mutex_;
|
std::mutex thread_message_mutex_;
|
||||||
std::mutex client_listener_mutex_;
|
std::mutex client_listener_mutex_;
|
||||||
std::list<std::vector<char>> data_to_client_;
|
std::list<std::vector<char>> data_to_client_;
|
||||||
|
std::string name_;
|
||||||
PyThreadState* py_thread_state_{};
|
PyThreadState* py_thread_state_{};
|
||||||
TimerList timers_;
|
TimerList timers_;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -15,15 +15,31 @@ namespace ballistica {
|
|||||||
using core::g_base_soft;
|
using core::g_base_soft;
|
||||||
using core::g_core;
|
using core::g_core;
|
||||||
|
|
||||||
|
bool FatalError::reported_{};
|
||||||
|
|
||||||
|
void FatalError::DoFatalError(const std::string& message) {
|
||||||
|
// Let the user and/or master-server know we're dying.
|
||||||
|
ReportFatalError(message, false);
|
||||||
|
|
||||||
|
// In some cases we prefer to cleanly exit the app with an error code
|
||||||
|
// in a way that won't wind up as a crash report; this avoids polluting
|
||||||
|
// our crash reports list with stuff from dev builds.
|
||||||
|
bool try_to_exit_cleanly =
|
||||||
|
!(core::g_base_soft && core::g_base_soft->IsUnmodifiedBlessedBuild());
|
||||||
|
bool handled = HandleFatalError(try_to_exit_cleanly, false);
|
||||||
|
if (!handled) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void FatalError::ReportFatalError(const std::string& message,
|
void FatalError::ReportFatalError(const std::string& message,
|
||||||
bool in_top_level_exception_handler) {
|
bool in_top_level_exception_handler) {
|
||||||
// We want to report the first fatal error that happens; if further ones
|
// We want to report only the first fatal error that happens; if further
|
||||||
// happen they are probably red herrings.
|
// ones happen they are likely red herrings triggered by the first.
|
||||||
static bool ran = false;
|
if (reported_) {
|
||||||
if (ran) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ran = true;
|
reported_ = true;
|
||||||
|
|
||||||
// Our main goal here varies based off whether we are an unmodified
|
// Our main goal here varies based off whether we are an unmodified
|
||||||
// blessed build. If we are, our main goal is to communicate as much info
|
// blessed build. If we are, our main goal is to communicate as much info
|
||||||
@ -139,8 +155,9 @@ void FatalError::DoBlockingFatalErrorDialog(const std::string& message) {
|
|||||||
bool* startedptr{&started};
|
bool* startedptr{&started};
|
||||||
bool* finishedptr{&finished};
|
bool* finishedptr{&finished};
|
||||||
|
|
||||||
// If our thread is holding the GIL, release it to give the main
|
// If our thread is holding the GIL, release it to give the main thread
|
||||||
// thread a better chance to get to the point of displaying the fatal error.
|
// a better chance of getting to the point of displaying the fatal
|
||||||
|
// error.
|
||||||
if (Python::HaveGIL()) {
|
if (Python::HaveGIL()) {
|
||||||
Python::PermanentlyReleaseGIL();
|
Python::PermanentlyReleaseGIL();
|
||||||
}
|
}
|
||||||
@ -152,7 +169,7 @@ void FatalError::DoBlockingFatalErrorDialog(const std::string& message) {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// Wait a short amount of time for the main thread to take action.
|
// Wait a short amount of time for the main thread to take action.
|
||||||
// There's a chance that it can't (if threads are paused, if it is
|
// There's a chance that it can't (if threads are suspended, if it is
|
||||||
// blocked on a synchronous call to another thread, etc.) so if we don't
|
// blocked on a synchronous call to another thread, etc.) so if we don't
|
||||||
// see something happening soon, just give up on showing a dialog.
|
// see something happening soon, just give up on showing a dialog.
|
||||||
auto starttime = core::CorePlatform::GetCurrentMillisecs();
|
auto starttime = core::CorePlatform::GetCurrentMillisecs();
|
||||||
@ -192,7 +209,7 @@ auto FatalError::HandleFatalError(bool exit_cleanly,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise its up to who called us (they might let the caught exception
|
// Otherwise its up to who called us (they might let the caught exception
|
||||||
// bubble up)
|
// bubble up).
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,24 +9,31 @@ namespace ballistica {
|
|||||||
|
|
||||||
class FatalError {
|
class FatalError {
|
||||||
public:
|
public:
|
||||||
|
/// Complete high level level fatal error call; does both reporting and
|
||||||
|
/// handling. ballistica::FatalError() simply calls this.
|
||||||
|
static void DoFatalError(const std::string& message);
|
||||||
|
|
||||||
/// Report a fatal error to the master-server/user/etc. Note that reporting
|
/// Report a fatal error to the master-server/user/etc. Note that reporting
|
||||||
/// only happens for the first invocation of this call; additional calls
|
/// only happens for the first invocation of this call; additional calls
|
||||||
/// are no-ops.
|
/// are no-ops. This is because the process of tearing down the app may
|
||||||
|
/// trigger additional errors which are red herrings.
|
||||||
static void ReportFatalError(const std::string& message,
|
static void ReportFatalError(const std::string& message,
|
||||||
bool in_top_level_exception_handler);
|
bool in_top_level_exception_handler);
|
||||||
|
|
||||||
/// Handle a fatal error. This can involve calling exit(), abort(), setting
|
/// Handle a fatal error. This can involve calling exit(), abort(),
|
||||||
/// up an asynchronous quit, etc. Returns true if the fatal-error has been
|
/// setting up an asynchronous quit, etc. Returns true if the fatal-error
|
||||||
/// handled; otherwise it is up to the caller (this should only be the case
|
/// has been handled; otherwise it is up to the caller (this should only
|
||||||
/// when in_top_level_exception_handler is true).
|
/// be the case when in_top_level_exception_handler is true).
|
||||||
/// Unlike ReportFatalError, the logic in this call can be invoked repeatedly
|
///
|
||||||
/// and should be prepared for that possibility in the case of recursive
|
/// Unlike ReportFatalError, the logic in this call can be invoked
|
||||||
/// fatal errors/etc.
|
/// repeatedly and should be prepared for that possibility in the case of
|
||||||
|
/// recursive fatal errors/etc.
|
||||||
static auto HandleFatalError(bool clean_exit,
|
static auto HandleFatalError(bool clean_exit,
|
||||||
bool in_top_level_exception_handler) -> bool;
|
bool in_top_level_exception_handler) -> bool;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void DoBlockingFatalErrorDialog(const std::string& message);
|
static void DoBlockingFatalErrorDialog(const std::string& message);
|
||||||
|
static bool reported_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ballistica
|
} // namespace ballistica
|
||||||
|
|||||||
@ -2,8 +2,13 @@
|
|||||||
|
|
||||||
#include "ballistica/shared/generic/runnable.h"
|
#include "ballistica/shared/generic/runnable.h"
|
||||||
|
|
||||||
|
#include "ballistica/core/core.h"
|
||||||
|
#include "ballistica/core/platform/core_platform.h"
|
||||||
|
|
||||||
namespace ballistica {
|
namespace ballistica {
|
||||||
|
|
||||||
|
using core::g_core;
|
||||||
|
|
||||||
auto Runnable::GetThreadOwnership() const -> Object::ThreadOwnership {
|
auto Runnable::GetThreadOwnership() const -> Object::ThreadOwnership {
|
||||||
return ThreadOwnership::kNextReferencing;
|
return ThreadOwnership::kNextReferencing;
|
||||||
}
|
}
|
||||||
@ -12,7 +17,14 @@ void Runnable::RunAndLogErrors() {
|
|||||||
try {
|
try {
|
||||||
Run();
|
Run();
|
||||||
} catch (const std::exception& exc) {
|
} catch (const std::exception& exc) {
|
||||||
Log(LogLevel::kError, std::string("Error in Runnable: ") + exc.what());
|
std::string type_name;
|
||||||
|
if (g_core != nullptr) {
|
||||||
|
type_name = g_core->platform->DemangleCXXSymbol(typeid(exc).name());
|
||||||
|
} else {
|
||||||
|
type_name = "<type unavailable>";
|
||||||
|
}
|
||||||
|
Log(LogLevel::kError,
|
||||||
|
std::string("Error in Runnable: " + type_name + ": ") + exc.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -718,7 +718,6 @@ def logcat() -> None:
|
|||||||
raise CleanError('Expected 2 args')
|
raise CleanError('Expected 2 args')
|
||||||
adb = sys.argv[2]
|
adb = sys.argv[2]
|
||||||
plat = sys.argv[3]
|
plat = sys.argv[3]
|
||||||
print('plat is', plat)
|
|
||||||
|
|
||||||
# My amazon tablet chokes on the color format.
|
# My amazon tablet chokes on the color format.
|
||||||
if plat == 'amazon':
|
if plat == 'amazon':
|
||||||
|
|||||||
@ -60,7 +60,12 @@ def build_openal(arch: str, mode: str) -> None:
|
|||||||
['git', 'clone', 'https://github.com/kcat/openal-soft.git', builddir],
|
['git', 'clone', 'https://github.com/kcat/openal-soft.git', builddir],
|
||||||
check=True,
|
check=True,
|
||||||
)
|
)
|
||||||
subprocess.run(['git', 'checkout', '1.23.1'], check=True, cwd=builddir)
|
# subprocess.run(['git', 'checkout', '1.23.1'], check=True, cwd=builddir)
|
||||||
|
subprocess.run(
|
||||||
|
['git', 'checkout', '5b5b948516f7340810ebbfdd5e46eb40f85d2e56'],
|
||||||
|
check=True,
|
||||||
|
cwd=builddir,
|
||||||
|
)
|
||||||
|
|
||||||
# Grab Oboe
|
# Grab Oboe
|
||||||
builddir_oboe = f'{builddir}_oboe'
|
builddir_oboe = f'{builddir}_oboe'
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user