mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-01-24 16:06:51 +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/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae",
|
||||
"build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599",
|
||||
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "dc1e98f8253f6e61583becda21c73162",
|
||||
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "c4e955cd85bc493fc03af74ce1062c66",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "29c7e4a4accdb87c354e6181780654d4",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "1e254a7d528cbaa33cf5bce8e8c4fea7",
|
||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "5b6792f0cadb52d82e483981b72def92",
|
||||
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "d5bdea21bb4fe28e55de0dae42b05c09",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "271c8808879bc09181f02c95787f6818",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "40fe07bb55d239e1eabdc0976b46112d",
|
||||
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "ae0f96fc3364de8f6721d12e775e0ab2",
|
||||
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "e2650e5017b27e84c398b894c768dee6",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "1a5d42f10fdd26ff5e81b07afa67c66e",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "940c21f7293968fe88023e33607d36f2",
|
||||
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "31f56d98b67c8894a2dfe73f4d4ed4c8",
|
||||
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "7b316468903c66cab07b0d8ae38b7bb7",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "42cd00bfbe63f750e9a064900582df48",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "045c0b16bf8ff0be028cefe5cfae1ee6",
|
||||
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "588711c1bb3cf986506b0a6a3d00fed9",
|
||||
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "a626549c279d83295e117835cc15050e",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "207ea740413855ede15da4ba1f304cf3",
|
||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "8516e63b6129b872ca59cdf5fade1c45",
|
||||
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "add3863bc3c332a1196db0673fde5587",
|
||||
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "e3c41c240bb333fc53240f64d6e9583e",
|
||||
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "add3863bc3c332a1196db0673fde5587",
|
||||
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "e3c41c240bb333fc53240f64d6e9583e",
|
||||
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "0b0ec8be8c575beba2935fdc9aa03ce5",
|
||||
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "04c21c4226944f71230420f9b399d1e4",
|
||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "0b0ec8be8c575beba2935fdc9aa03ce5",
|
||||
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "04c21c4226944f71230420f9b399d1e4",
|
||||
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "e80ffa41dcc78dbd75baf89fadb096b4",
|
||||
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "6514628d8ce1c046726de69fa0086613",
|
||||
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "e80ffa41dcc78dbd75baf89fadb096b4",
|
||||
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "6514628d8ce1c046726de69fa0086613",
|
||||
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "9f4f5c5043d66fa3bdeb16b0599b5de4",
|
||||
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "ddcb6a381ef5eaef6b1650cd94c498fc",
|
||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "a3124c863c4b80de5acc20a7c9d49492",
|
||||
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "ddcb6a381ef5eaef6b1650cd94c498fc",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "155cfc2d5a62f02ab4490b24afe241e2",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "9c5b8303df63e5bdde524ae4ce654fa8",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "6895c7a2714c09e065ba6ac1f3860501",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "a0a6f9d1afaacc9c762da68df3f9178c",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "0ce476258657733e63c21be416d35574",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "e23b83abb5efcb2566e003be2c6a4b6e",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "364dd18a7b830ec1600827734c8ac0e6",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "ab27a9c76a3706803283e09a43ecd92b",
|
||||
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "be3e38d0503d272d22a9226323803564",
|
||||
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "feea24dbb10aec67e1aa4d6c1d0928b5",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "ac035cdcd1134ed3eefb50f2b6fee259",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "aa71c8cf97c379aa2cc93b3edb050167",
|
||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "afaf8cc0bd7e93439366a43a80debdbc",
|
||||
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "f787ba6f598297a6b43668949af0c8e2",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "f8317a770d865a1380bfef13011ae1cc",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "39afba54e9822911d8bfdf53602fcc8d",
|
||||
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "2fe24a441e96e882ce29cd55c886ee8e",
|
||||
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "4d068a5426f7e8814438a295f34d5356",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "e315d748000555f280fd6a0e9575f799",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "753a957f642173a1568725ff0b36bfce",
|
||||
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "833be86309d39cfb7f273c4c72ffdafc",
|
||||
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "49ad9d968f4d615323a840f036caa36f",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "5795639a921b758c08a14ad09a8883d9",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "db31fcbdeeb8f5ab794a1f9f1dfe7ede",
|
||||
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "8aa9aa6b833d64d2e4a3689f6ce134fb",
|
||||
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "7f6119e33678b934ac1c5f8002d8d6bc",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "639a01a7dcfd2f6db3023b610ad44d4a",
|
||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "e925ad3d39bb366f78c6e0f9da22d0cb",
|
||||
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "4f5d3cafbea651078c1721684b61033a",
|
||||
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "7436c575aee1f9d112d41f18e2ae6b22",
|
||||
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "4f5d3cafbea651078c1721684b61033a",
|
||||
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "7436c575aee1f9d112d41f18e2ae6b22",
|
||||
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "3ffcb35eb71566fefb7d9ad2589b37b4",
|
||||
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "ce9de33efccb9aa1e301fe4f11e6b1c1",
|
||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "3ffcb35eb71566fefb7d9ad2589b37b4",
|
||||
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "ce9de33efccb9aa1e301fe4f11e6b1c1",
|
||||
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "9d674b5e8a8357b9462a65359611ea45",
|
||||
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "e6d1c0b9bf27c34968e512c5db8e7de5",
|
||||
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "9d674b5e8a8357b9462a65359611ea45",
|
||||
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "e6d1c0b9bf27c34968e512c5db8e7de5",
|
||||
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "910b5e39fe4ba5bb849505c578efe3ec",
|
||||
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "2d9e14f7cfe50b1dc51e5e9eae05b5fd",
|
||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "b83a67eeaed0fc99bf995767a8150e5d",
|
||||
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "2d9e14f7cfe50b1dc51e5e9eae05b5fd",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "8c6063999e8d77daf8bf8a33d045c737",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "e60f8d06f584bac8e54d920776a4d57d",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "89b61adb57d6a53740f302d01e107587",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "f347b87f6ee2167546edec41b750245c",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "ed74c7fd0a854650675aa92366c2be3f",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "ac5046013922b1db4689c6ba653791c1",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "52c36bf974594558a33f7c6caa07b81e",
|
||||
"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/enums.py": "28323912b56ec07701eda3d41a6a4101",
|
||||
"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.
|
||||
- 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,8 +15,14 @@
|
||||
- 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
|
||||
@ -31,6 +37,12 @@
|
||||
`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)
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@ if TYPE_CHECKING:
|
||||
from babase._login import LoginAdapter, LoginInfo
|
||||
|
||||
|
||||
DEBUG_LOG = False
|
||||
DEBUG_LOG = _babase.temp_testing()
|
||||
|
||||
|
||||
class AccountV2Subsystem:
|
||||
@ -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(
|
||||
|
||||
@ -17,7 +17,7 @@ if TYPE_CHECKING:
|
||||
from typing import Callable
|
||||
|
||||
|
||||
DEBUG_LOG = False
|
||||
DEBUG_LOG = _babase.temp_testing()
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -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.
|
||||
|
||||
@ -52,7 +52,7 @@ if TYPE_CHECKING:
|
||||
|
||||
# Build number and version of the ballistica binary we expect to be
|
||||
# using.
|
||||
TARGET_BALLISTICA_BUILD = 21657
|
||||
TARGET_BALLISTICA_BUILD = 21693
|
||||
TARGET_BALLISTICA_VERSION = '1.7.30'
|
||||
|
||||
|
||||
|
||||
@ -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')
|
||||
|
||||
|
||||
@ -209,8 +209,12 @@ 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
|
||||
)
|
||||
|
||||
@ -482,9 +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(),
|
||||
from_window=self.root_widget,
|
||||
from_window=False, # Disable this test.
|
||||
)
|
||||
self._transition_out(transition='out_left')
|
||||
if self._delegate is not None:
|
||||
|
||||
@ -257,7 +257,7 @@ class ProfileBrowserWindow(bui.Window):
|
||||
EditProfileWindow(
|
||||
existing_profile=None, in_main_menu=self._in_main_menu
|
||||
).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:
|
||||
@ -323,7 +323,7 @@ class ProfileBrowserWindow(bui.Window):
|
||||
EditProfileWindow(
|
||||
self._selected_profile, in_main_menu=self._in_main_menu
|
||||
).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:
|
||||
|
||||
@ -151,8 +151,6 @@ void AppAdapter::NativeReviewRequest() {
|
||||
|
||||
void AppAdapter::DoNativeReviewRequest() { FatalError("Fixme unimplemented."); }
|
||||
|
||||
auto AppAdapter::ShouldSilenceAudioWhenInactive() -> bool const {
|
||||
return false;
|
||||
}
|
||||
auto AppAdapter::ShouldSilenceAudioForInactive() -> bool const { return false; }
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
@ -135,11 +135,14 @@ 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 is 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.
|
||||
virtual auto ShouldSilenceAudioWhenInactive() -> 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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -28,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};
|
||||
@ -108,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_server_{};
|
||||
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
|
||||
@ -156,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());
|
||||
|
||||
@ -168,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.
|
||||
@ -212,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
|
||||
}
|
||||
|
||||
@ -260,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
|
||||
}
|
||||
|
||||
@ -335,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->LowLevelDebugLog(
|
||||
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->LowLevelDebugLog("UNKNOWN EXC pausing alcDevice");
|
||||
throw;
|
||||
Log(LogLevel::kError, "Unknown error in alcDevicePauseSOFT");
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -373,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->LowLevelDebugLog(
|
||||
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->LowLevelDebugLog("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;
|
||||
@ -478,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_();
|
||||
});
|
||||
}
|
||||
@ -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_() {
|
||||
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.
|
||||
if (!suspended_ && !shutting_down_) {
|
||||
ProcessDeviceDisconnects_(real_time_seconds);
|
||||
|
||||
// Do some loading...
|
||||
have_pending_loads_ = g_base->assets->RunPendingAudioLoads();
|
||||
|
||||
@ -699,29 +788,28 @@ 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).
|
||||
// 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;
|
||||
if (g_base->app_adapter->ShouldSilenceAudioWhenInactive()) {
|
||||
app_active_volume_ = app_active ? 1.0f : 0.0f;
|
||||
} else {
|
||||
app_active_volume_ = 1.0f;
|
||||
}
|
||||
app_active_volume_ =
|
||||
(!app_active && g_base->app_adapter->ShouldSilenceAudioForInactive())
|
||||
? 0.0f
|
||||
: 1.0f;
|
||||
for (auto&& i : sources_) {
|
||||
i->UpdateVolume();
|
||||
}
|
||||
@ -797,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)));
|
||||
}
|
||||
@ -882,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;
|
||||
}
|
||||
@ -895,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_;
|
||||
@ -1244,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);
|
||||
@ -122,7 +125,11 @@ class AudioServer {
|
||||
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.
|
||||
@ -146,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
|
||||
|
||||
@ -198,6 +198,8 @@ void BaseFeatureSet::StartApp() {
|
||||
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 +250,17 @@ 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() {
|
||||
@ -890,8 +903,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()
|
||||
|
||||
@ -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();
|
||||
@ -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); });
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -33,12 +33,10 @@ void NetworkReader::OnAppSuspend() {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1788,6 +1788,32 @@ static PyMethodDef PyNativeReviewRequestDef = {
|
||||
"\n"
|
||||
"(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> {
|
||||
@ -1856,6 +1882,7 @@ auto PythonMethodsMisc::GetMethods() -> std::vector<PyMethodDef> {
|
||||
PyUsingGameCenterDef,
|
||||
PyNativeReviewRequestSupportedDef,
|
||||
PyNativeReviewRequestDef,
|
||||
PyTempTestingDef,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -236,9 +236,6 @@ void UI::MainMenuPress_(InputDevice* device) {
|
||||
assert(g_base->InLogicThread());
|
||||
if (auto* ui_delegate = g_base->ui->delegate()) {
|
||||
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{};
|
||||
int master_server_source{};
|
||||
std::vector<EventLoop*> suspendable_event_loops;
|
||||
std::mutex v1_cloud_log_mutex;
|
||||
std::string v1_cloud_log;
|
||||
std::mutex thread_name_map_mutex;
|
||||
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
|
||||
std::mutex object_list_mutex;
|
||||
|
||||
@ -39,7 +39,7 @@ auto main(int argc, char** argv) -> int {
|
||||
namespace ballistica {
|
||||
|
||||
// These are set automatically via script; don't modify them here.
|
||||
const int kEngineBuildNumber = 21657;
|
||||
const int kEngineBuildNumber = 21693;
|
||||
const char* kEngineVersion = "1.7.30";
|
||||
const int kEngineApiVersion = 8;
|
||||
|
||||
@ -53,6 +53,8 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int {
|
||||
core::BaseSoftInterface* l_base{};
|
||||
|
||||
try {
|
||||
auto time1 = core::CorePlatform::GetCurrentMillisecs();
|
||||
|
||||
// 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.
|
||||
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.
|
||||
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
|
||||
// simply as a Python interpreter in that case; we don't do any
|
||||
// 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.
|
||||
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
|
||||
// dependency to it. Let's see if it's available.
|
||||
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.");
|
||||
}
|
||||
|
||||
auto time4 = core::CorePlatform::GetCurrentMillisecs();
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 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
|
||||
// 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()) {
|
||||
// In environments where we control the event loop, do that.
|
||||
l_base->RunAppToCompletion();
|
||||
@ -130,20 +155,20 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int {
|
||||
std::string error_msg =
|
||||
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);
|
||||
|
||||
// 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.
|
||||
// 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 = !(l_base && l_base->IsUnmodifiedBlessedBuild());
|
||||
|
||||
// If this is true it means the app is handling things (showing a fatal
|
||||
// error dialog, etc.) and it's out of our hands.
|
||||
// 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);
|
||||
|
||||
// Do the default thing if it's not been handled.
|
||||
// If it's not been handled, take the app down ourself.
|
||||
if (!handled) {
|
||||
if (try_to_exit_cleanly) {
|
||||
exit(1);
|
||||
@ -155,22 +180,95 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int {
|
||||
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
|
||||
|
||||
void FatalError(const std::string& message) {
|
||||
// Let the user and/or master-server know we're dying.
|
||||
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.");
|
||||
}
|
||||
FatalError::DoFatalError(message);
|
||||
}
|
||||
|
||||
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
|
||||
/// running.
|
||||
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
|
||||
|
||||
// Print a momentary message on the screen.
|
||||
|
||||
@ -342,47 +342,46 @@ void EventLoop::GetThreadMessages_(std::list<ThreadMessage_>* messages) {
|
||||
void EventLoop::BootstrapThread_() {
|
||||
assert(!bootstrapped_);
|
||||
thread_id_ = std::this_thread::get_id();
|
||||
const char* name;
|
||||
const char* id_string;
|
||||
|
||||
switch (identifier_) {
|
||||
case EventLoopID::kLogic:
|
||||
name = "logic";
|
||||
name_ = "logic";
|
||||
id_string = "ballistica logic";
|
||||
break;
|
||||
case EventLoopID::kStdin:
|
||||
name = "stdin";
|
||||
name_ = "stdin";
|
||||
id_string = "ballistica stdin";
|
||||
break;
|
||||
case EventLoopID::kAssets:
|
||||
name = "assets";
|
||||
name_ = "assets";
|
||||
id_string = "ballistica assets";
|
||||
break;
|
||||
case EventLoopID::kFileOut:
|
||||
name = "fileout";
|
||||
name_ = "fileout";
|
||||
id_string = "ballistica file-out";
|
||||
break;
|
||||
case EventLoopID::kMain:
|
||||
name = "main";
|
||||
name_ = "main";
|
||||
id_string = "ballistica main";
|
||||
break;
|
||||
case EventLoopID::kAudio:
|
||||
name = "audio";
|
||||
name_ = "audio";
|
||||
id_string = "ballistica audio";
|
||||
break;
|
||||
case EventLoopID::kBGDynamics:
|
||||
name = "bgdynamics";
|
||||
name_ = "bgdynamics";
|
||||
id_string = "ballistica bg-dynamics";
|
||||
break;
|
||||
case EventLoopID::kNetworkWrite:
|
||||
name = "networkwrite";
|
||||
name_ = "networkwrite";
|
||||
id_string = "ballistica network-write";
|
||||
break;
|
||||
default:
|
||||
throw Exception();
|
||||
}
|
||||
assert(name && id_string);
|
||||
SetInternalThreadName_(name);
|
||||
assert(!name_.empty() && id_string);
|
||||
SetInternalThreadName_(name_);
|
||||
|
||||
// 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.
|
||||
@ -552,8 +551,7 @@ void EventLoop::PushThreadMessage_(const ThreadMessage_& t) {
|
||||
if (!sent_error) {
|
||||
sent_error = true;
|
||||
log_entries.emplace_back(
|
||||
LogLevel::kError,
|
||||
"ThreadMessage list > 1000 in thread: " + CurrentThreadName());
|
||||
LogLevel::kError, "ThreadMessage list > 1000 in thread: " + name_);
|
||||
|
||||
LogThreadMessageTally_(&log_entries);
|
||||
}
|
||||
@ -561,8 +559,7 @@ void EventLoop::PushThreadMessage_(const ThreadMessage_& t) {
|
||||
|
||||
// Prevent runaway mem usage if the list gets out of control.
|
||||
if (thread_messages_.size() > 10000) {
|
||||
FatalError("ThreadMessage list > 10000 in thread: "
|
||||
+ CurrentThreadName());
|
||||
FatalError("ThreadMessage list > 10000 in thread: " + name_);
|
||||
}
|
||||
|
||||
// Unlock thread-message list and inform thread that there's something
|
||||
|
||||
@ -97,6 +97,8 @@ class EventLoop {
|
||||
auto suspended() { return suspended_; }
|
||||
auto done() -> bool { return done_; }
|
||||
|
||||
auto name() const { return name_; }
|
||||
|
||||
private:
|
||||
struct ThreadMessage_ {
|
||||
enum class Type { kShutdown = 999, kRunnable, kSuspend, kUnsuspend };
|
||||
@ -149,13 +151,6 @@ class EventLoop {
|
||||
|
||||
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};
|
||||
ThreadSource source_{};
|
||||
bool bootstrapped_{};
|
||||
@ -173,6 +168,7 @@ class EventLoop {
|
||||
std::mutex thread_message_mutex_;
|
||||
std::mutex client_listener_mutex_;
|
||||
std::list<std::vector<char>> data_to_client_;
|
||||
std::string name_;
|
||||
PyThreadState* py_thread_state_{};
|
||||
TimerList timers_;
|
||||
};
|
||||
|
||||
@ -15,15 +15,31 @@ namespace ballistica {
|
||||
using core::g_base_soft;
|
||||
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,
|
||||
bool in_top_level_exception_handler) {
|
||||
// We want to report the first fatal error that happens; if further ones
|
||||
// happen they are probably red herrings.
|
||||
static bool ran = false;
|
||||
if (ran) {
|
||||
// We want to report only the first fatal error that happens; if further
|
||||
// ones happen they are likely red herrings triggered by the first.
|
||||
if (reported_) {
|
||||
return;
|
||||
}
|
||||
ran = true;
|
||||
reported_ = true;
|
||||
|
||||
// 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
|
||||
@ -139,8 +155,9 @@ void FatalError::DoBlockingFatalErrorDialog(const std::string& message) {
|
||||
bool* startedptr{&started};
|
||||
bool* finishedptr{&finished};
|
||||
|
||||
// If our thread is holding the GIL, release it to give the main
|
||||
// thread a better chance to get to the point of displaying the fatal error.
|
||||
// If our thread is holding the GIL, release it to give the main thread
|
||||
// a better chance of getting to the point of displaying the fatal
|
||||
// error.
|
||||
if (Python::HaveGIL()) {
|
||||
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.
|
||||
// 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
|
||||
// see something happening soon, just give up on showing a dialog.
|
||||
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
|
||||
// bubble up)
|
||||
// bubble up).
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -9,24 +9,31 @@ namespace ballistica {
|
||||
|
||||
class FatalError {
|
||||
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
|
||||
/// 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,
|
||||
bool in_top_level_exception_handler);
|
||||
|
||||
/// Handle a fatal error. This can involve calling exit(), abort(), setting
|
||||
/// up an asynchronous quit, etc. Returns true if the fatal-error has been
|
||||
/// handled; otherwise it is up to the caller (this should only 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
|
||||
/// fatal errors/etc.
|
||||
/// Handle a fatal error. This can involve calling exit(), abort(),
|
||||
/// setting up an asynchronous quit, etc. Returns true if the fatal-error
|
||||
/// has been handled; otherwise it is up to the caller (this should only
|
||||
/// 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 fatal errors/etc.
|
||||
static auto HandleFatalError(bool clean_exit,
|
||||
bool in_top_level_exception_handler) -> bool;
|
||||
|
||||
private:
|
||||
static void DoBlockingFatalErrorDialog(const std::string& message);
|
||||
static bool reported_;
|
||||
};
|
||||
|
||||
} // namespace ballistica
|
||||
|
||||
@ -2,8 +2,13 @@
|
||||
|
||||
#include "ballistica/shared/generic/runnable.h"
|
||||
|
||||
#include "ballistica/core/core.h"
|
||||
#include "ballistica/core/platform/core_platform.h"
|
||||
|
||||
namespace ballistica {
|
||||
|
||||
using core::g_core;
|
||||
|
||||
auto Runnable::GetThreadOwnership() const -> Object::ThreadOwnership {
|
||||
return ThreadOwnership::kNextReferencing;
|
||||
}
|
||||
@ -12,7 +17,14 @@ void Runnable::RunAndLogErrors() {
|
||||
try {
|
||||
Run();
|
||||
} 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')
|
||||
adb = sys.argv[2]
|
||||
plat = sys.argv[3]
|
||||
print('plat is', plat)
|
||||
|
||||
# My amazon tablet chokes on the color format.
|
||||
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],
|
||||
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
|
||||
builddir_oboe = f'{builddir}_oboe'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user